- {connector.connector_type === "MCP_CONNECTOR" ? "MCP Server" : connector.name}
+ {connector.name}
Manage your connector settings and sync configuration
diff --git a/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts b/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts
index 7a2243705..4f56f588d 100644
--- a/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts
+++ b/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts
@@ -646,7 +646,7 @@ export const useConnectorDialog = () => {
const successMessage =
currentConnectorType === "MCP_CONNECTOR"
- ? `${connector.name} MCP server added successfully`
+ ? `${connector.name} added successfully`
: `${connectorTitle} connected and indexing started!`;
toast.success(successMessage, {
description: periodicEnabledForIndexing
@@ -711,7 +711,7 @@ export const useConnectorDialog = () => {
// Other non-indexable connectors - just show success message and close
const successMessage =
currentConnectorType === "MCP_CONNECTOR"
- ? `${connector.name} MCP server added successfully`
+ ? `${connector.name} added successfully`
: `${connectorTitle} connected successfully!`;
toast.success(successMessage);
diff --git a/surfsense_web/components/assistant-ui/connector-popup/tabs/all-connectors-tab.tsx b/surfsense_web/components/assistant-ui/connector-popup/tabs/all-connectors-tab.tsx
index 6152504fc..2487b7276 100644
--- a/surfsense_web/components/assistant-ui/connector-popup/tabs/all-connectors-tab.tsx
+++ b/surfsense_web/components/assistant-ui/connector-popup/tabs/all-connectors-tab.tsx
@@ -1,6 +1,7 @@
"use client";
import type { FC } from "react";
+import { EnumConnectorName } from "@/contracts/enums/connector";
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
import { ConnectorCard } from "../components/connector-card";
import { CRAWLERS, OAUTH_CONNECTORS, OTHER_CONNECTORS } from "../constants/connector-constants";
@@ -161,6 +162,16 @@ export const AllConnectorsTab: FC = ({
);
const isIndexing = actualConnector && indexingConnectorIds?.has(actualConnector.id);
+ // For MCP connectors, count total MCP connectors instead of document count
+ const isMCP = connector.connectorType === EnumConnectorName.MCP_CONNECTOR;
+ const mcpConnectorCount =
+ isMCP && allConnectors
+ ? allConnectors.filter(
+ (c: SearchSourceConnector) =>
+ c.connector_type === EnumConnectorName.MCP_CONNECTOR
+ ).length
+ : undefined;
+
const handleConnect = onConnectNonOAuth
? () => onConnectNonOAuth(connector.connectorType)
: () => {}; // Fallback - connector popup should handle all connector types
@@ -175,6 +186,7 @@ export const AllConnectorsTab: FC = ({
isConnected={isConnected}
isConnecting={isConnecting}
documentCount={documentCount}
+ connectorCount={mcpConnectorCount}
isIndexing={isIndexing}
onConnect={handleConnect}
onManage={
diff --git a/surfsense_web/components/assistant-ui/connector-popup/utils/mcp-config-validator.ts b/surfsense_web/components/assistant-ui/connector-popup/utils/mcp-config-validator.ts
index e03d76445..650a95e3d 100644
--- a/surfsense_web/components/assistant-ui/connector-popup/utils/mcp-config-validator.ts
+++ b/surfsense_web/components/assistant-ui/connector-popup/utils/mcp-config-validator.ts
@@ -138,35 +138,37 @@ export const parseMCPConfig = (configJson: string): MCPConfigValidationResult =>
// Replace technical error messages with user-friendly ones
if (errorMsg.includes("expected string, received undefined")) {
- errorMsg = "This field is required";
+ errorMsg = fieldPath ? `The '${fieldPath}' field is required` : "This field is required";
} else if (errorMsg.includes("Invalid input")) {
- errorMsg = "Invalid value";
+ errorMsg = fieldPath ? `The '${fieldPath}' field has an invalid value` : "Invalid value";
+ } else if (fieldPath && !errorMsg.toLowerCase().includes(fieldPath.toLowerCase())) {
+ // If error message doesn't mention the field name, prepend it
+ errorMsg = `The '${fieldPath}' field: ${errorMsg}`;
}
- const formattedError = fieldPath ? `${fieldPath}: ${errorMsg}` : errorMsg;
-
- console.error("[MCP Validator] ❌ Validation error:", formattedError);
+ console.error("[MCP Validator] ❌ Validation error:", errorMsg);
console.error("[MCP Validator] Full Zod errors:", result.error.issues);
return {
config: null,
- error: formattedError,
+ error: errorMsg,
};
}
// Build config based on transport type
- const config: MCPServerConfig = result.data.transport === "stdio" || !result.data.transport
- ? {
- command: (result.data as z.infer).command,
- args: (result.data as z.infer).args,
- env: (result.data as z.infer).env,
- transport: "stdio" as const,
- }
- : {
- url: (result.data as z.infer).url,
- headers: (result.data as z.infer).headers,
- transport: result.data.transport as "streamable-http" | "http" | "sse",
- };
+ const config: MCPServerConfig =
+ result.data.transport === "stdio" || !result.data.transport
+ ? {
+ command: (result.data as z.infer).command,
+ args: (result.data as z.infer).args,
+ env: (result.data as z.infer).env,
+ transport: "stdio" as const,
+ }
+ : {
+ url: (result.data as z.infer).url,
+ headers: (result.data as z.infer).headers,
+ transport: result.data.transport as "streamable-http" | "http" | "sse",
+ };
// Cache the successfully parsed config
configCache.set(configJson, {
diff --git a/surfsense_web/components/assistant-ui/connector-popup/views/connector-accounts-list-view.tsx b/surfsense_web/components/assistant-ui/connector-popup/views/connector-accounts-list-view.tsx
index 5f8c1f3ed..a48ca02e6 100644
--- a/surfsense_web/components/assistant-ui/connector-popup/views/connector-accounts-list-view.tsx
+++ b/surfsense_web/components/assistant-ui/connector-popup/views/connector-accounts-list-view.tsx
@@ -1,9 +1,10 @@
"use client";
import { differenceInDays, differenceInMinutes, format, isToday, isYesterday } from "date-fns";
-import { ArrowLeft, Loader2, Plus } from "lucide-react";
+import { ArrowLeft, Loader2, Plus, Server } from "lucide-react";
import type { FC } from "react";
import { Button } from "@/components/ui/button";
+import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
import { cn } from "@/lib/utils";
@@ -19,6 +20,7 @@ interface ConnectorAccountsListViewProps {
onManage: (connector: SearchSourceConnector) => void;
onAddAccount: () => void;
isConnecting?: boolean;
+ addButtonText?: string;
}
/**
@@ -70,6 +72,7 @@ export const ConnectorAccountsListView: FC = ({
onManage,
onAddAccount,
isConnecting = false,
+ addButtonText,
}) => {
// Get connector status
const { isConnectorEnabled, getConnectorStatusMessage } = useConnectorStatus();
@@ -80,6 +83,22 @@ export const ConnectorAccountsListView: FC = ({
// Filter connectors to only show those of this type
const typeConnectors = connectors.filter((c) => c.connector_type === connectorType);
+ // Determine button text - default to "Add Account" unless specified
+ const buttonText =
+ addButtonText ||
+ (connectorType === EnumConnectorName.MCP_CONNECTOR ? "Add New MCP Server" : "Add Account");
+ const isMCP = connectorType === EnumConnectorName.MCP_CONNECTOR;
+
+ // Helper to get display name for connector (handles MCP server name extraction)
+ const getDisplayName = (connector: SearchSourceConnector): string => {
+ if (isMCP) {
+ // For MCP, extract server name from config if available
+ const serverName = connector.config?.server_config?.name || connector.name;
+ return serverName;
+ }
+ return getConnectorDisplayName(connector.name);
+ };
+
return (
{/* Header */}
@@ -115,22 +134,22 @@ export const ConnectorAccountsListView: FC
= ({
onClick={onAddAccount}
disabled={isConnecting || !isEnabled}
className={cn(
- "flex items-center gap-1.5 sm:gap-2 px-2 sm:px-3 py-1.5 sm:py-2 rounded-lg border-2 border-dashed text-left transition-all duration-200 shrink-0 self-center sm:self-auto sm:w-auto",
+ "flex items-center justify-center gap-1.5 h-8 px-3 rounded-md border-2 border-dashed text-xs sm:text-sm transition-all duration-200 shrink-0 w-full sm:w-auto",
!isEnabled
? "border-border/30 opacity-50 cursor-not-allowed"
- : "border-primary/50 hover:bg-primary/5",
+ : "border-slate-400/20 dark:border-white/20 hover:bg-primary/5",
isConnecting && "opacity-50 cursor-not-allowed"
)}
>
-
+
{isConnecting ? (
-
+
) : (
-
+
)}
-
- {isConnecting ? "Connecting" : "Add Account"}
+
+ {isConnecting ? "Connecting" : buttonText}
@@ -139,61 +158,81 @@ export const ConnectorAccountsListView: FC = ({
{/* Content */}
{/* Connected Accounts Grid */}
-
- {typeConnectors.map((connector) => {
- const isIndexing = indexingConnectorIds.has(connector.id);
+ {typeConnectors.length === 0 ? (
+
+
+ {isMCP ? (
+
+ ) : (
+ getConnectorIcon(connectorType, "size-8")
+ )}
+
+
+ {isMCP ? "No MCP Servers" : `No ${connectorTitle} Accounts`}
+
+
+ {isMCP
+ ? "Get started by adding your first Model Context Protocol server"
+ : `Get started by connecting your first ${connectorTitle} account`}
+
+
+ ) : (
+
+ {typeConnectors.map((connector) => {
+ const isIndexing = indexingConnectorIds.has(connector.id);
- return (
-
+ return (
- {getConnectorIcon(connector.connector_type, "size-6")}
-
-
-
- {getConnectorDisplayName(connector.name)}
-
- {isIndexing ? (
-
-
- Syncing
+
+ {getConnectorIcon(connector.connector_type, "size-6")}
+
+
+
+ {getDisplayName(connector)}
- ) : (
-
- {isIndexableConnector(connector.connector_type)
- ? connector.last_indexed_at
- ? `Last indexed: ${formatLastIndexedDate(connector.last_indexed_at)}`
- : "Never indexed"
- : "Active"}
-
- )}
+ {isIndexing ? (
+
+
+ Syncing
+
+ ) : (
+
+ {isIndexableConnector(connector.connector_type)
+ ? connector.last_indexed_at
+ ? `Last indexed: ${formatLastIndexedDate(connector.last_indexed_at)}`
+ : "Never indexed"
+ : "Active"}
+
+ )}
+
+
-
-
- );
- })}
-
+ );
+ })}
+
+ )}
);
diff --git a/surfsense_web/components/assistant-ui/connector-popup/views/mcp-connector-list-view.tsx b/surfsense_web/components/assistant-ui/connector-popup/views/mcp-connector-list-view.tsx
deleted file mode 100644
index 78a0b0b0c..000000000
--- a/surfsense_web/components/assistant-ui/connector-popup/views/mcp-connector-list-view.tsx
+++ /dev/null
@@ -1,134 +0,0 @@
-"use client";
-
-import { Plus, Server, XCircle } from "lucide-react";
-import type { FC } from "react";
-import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
-import { Button } from "@/components/ui/button";
-import { EnumConnectorName } from "@/contracts/enums/connector";
-import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
-import type { SearchSourceConnector } from "@/contracts/types/connector.types";
-import { cn } from "@/lib/utils";
-
-interface MCPConnectorListViewProps {
- mcpConnectors: SearchSourceConnector[];
- onAddNew: () => void;
- onManageConnector: (connector: SearchSourceConnector) => void;
- onBack: () => void;
-}
-
-export const MCPConnectorListView: FC = ({
- mcpConnectors,
- onAddNew,
- onManageConnector,
- onBack,
-}) => {
- // Validate that all connectors are MCP connectors
- const invalidConnectors = mcpConnectors.filter(
- (c) => c.connector_type !== EnumConnectorName.MCP_CONNECTOR
- );
-
- if (invalidConnectors.length > 0) {
- console.error(
- "MCPConnectorListView received non-MCP connectors:",
- invalidConnectors.map((c) => c.connector_type)
- );
- return (
-
-
- Invalid Connector Type
-
- This view can only display MCP connectors. Found {invalidConnectors.length} invalid
- connector(s).
-
-
- );
- }
- return (
-
- {/* Header */}
-
-
-
-
-
MCP Connectors
-
- Manage your Model Context Protocol servers
-
-
-
-
-
- {/* Add New Button */}
-
-
-
-
- {/* MCP Connectors List */}
-
- {mcpConnectors.length === 0 ? (
-
-
-
-
-
No MCP Servers
-
- Get started by adding your first Model Context Protocol server
-
-
- ) : (
- mcpConnectors.map((connector) => {
- // Extract server name from config
- const serverName = connector.config?.server_config?.name || connector.name;
-
- return (
-
-
- {getConnectorIcon("MCP_CONNECTOR", "size-6")}
-
-
-
-
- );
- })
- )}
-
-
- );
-};
diff --git a/surfsense_web/contracts/enums/connectorIcons.tsx b/surfsense_web/contracts/enums/connectorIcons.tsx
index 5f147b63b..9350b6a1e 100644
--- a/surfsense_web/contracts/enums/connectorIcons.tsx
+++ b/surfsense_web/contracts/enums/connectorIcons.tsx
@@ -65,7 +65,7 @@ export const getConnectorIcon = (connectorType: EnumConnectorName | string, clas
case EnumConnectorName.CIRCLEBACK_CONNECTOR:
return ;
case EnumConnectorName.MCP_CONNECTOR:
- return ;
+ return ;
// Additional cases for non-enum connector types
case "YOUTUBE_CONNECTOR":
return ;
diff --git a/surfsense_web/public/connectors/modelcontextprotocol.svg b/surfsense_web/public/connectors/modelcontextprotocol.svg
new file mode 100644
index 000000000..e9c3fa46e
--- /dev/null
+++ b/surfsense_web/public/connectors/modelcontextprotocol.svg
@@ -0,0 +1 @@
+
\ No newline at end of file