mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-06 20:15:17 +02:00
Merge pull request #711 from AnishSarkar22/fix/SUR-86
fix: UI overlap of thinking step, added animation for thinking step
This commit is contained in:
commit
7435fdb8f8
14 changed files with 201 additions and 192 deletions
|
|
@ -102,7 +102,8 @@ export const ConnectorIndicator: FC = () => {
|
||||||
// Fallback to API if Electric is not available or fails
|
// Fallback to API if Electric is not available or fails
|
||||||
// Use Electric data if: 1) we have data, or 2) still loading without error
|
// Use Electric data if: 1) we have data, or 2) still loading without error
|
||||||
// Use API data if: Electric failed (has error) or finished loading with no data
|
// Use API data if: Electric failed (has error) or finished loading with no data
|
||||||
const useElectricData = connectorsFromElectric.length > 0 || (connectorsLoading && !connectorsError);
|
const useElectricData =
|
||||||
|
connectorsFromElectric.length > 0 || (connectorsLoading && !connectorsError);
|
||||||
const connectors = useElectricData ? connectorsFromElectric : allConnectors || [];
|
const connectors = useElectricData ? connectorsFromElectric : allConnectors || [];
|
||||||
|
|
||||||
// Manual refresh function that works with both Electric and API
|
// Manual refresh function that works with both Electric and API
|
||||||
|
|
@ -229,7 +230,6 @@ export const ConnectorIndicator: FC = () => {
|
||||||
isDisconnecting={isDisconnecting}
|
isDisconnecting={isDisconnecting}
|
||||||
isIndexing={indexingConnectorIds.has(editingConnector.id)}
|
isIndexing={indexingConnectorIds.has(editingConnector.id)}
|
||||||
searchSpaceId={searchSpaceId?.toString()}
|
searchSpaceId={searchSpaceId?.toString()}
|
||||||
|
|
||||||
onStartDateChange={setStartDate}
|
onStartDateChange={setStartDate}
|
||||||
onEndDateChange={setEndDate}
|
onEndDateChange={setEndDate}
|
||||||
onPeriodicEnabledChange={setPeriodicEnabled}
|
onPeriodicEnabledChange={setPeriodicEnabled}
|
||||||
|
|
|
||||||
|
|
@ -122,11 +122,12 @@ export const MCPConnectForm: FC<ConnectFormProps> = ({ onSubmit, isSubmitting })
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 pb-6">
|
<div className="space-y-6 pb-6">
|
||||||
<Alert className="bg-slate-400/5 dark:bg-white/5 border-slate-400/20 p-2 sm:p-3 [&>svg]:top-2 sm:[&>svg]:top-3">
|
<Alert className="bg-slate-400/5 dark:bg-white/5 border-slate-400/20 p-2 sm:p-3 [&>svg]:top-2 sm:[&>svg]:top-3">
|
||||||
<Server className="h-4 w-4 shrink-0" />
|
<Server className="h-4 w-4 shrink-0" />
|
||||||
<AlertDescription className="text-[10px] sm:text-xs">
|
<AlertDescription className="text-[10px] sm:text-xs">
|
||||||
Connect to an MCP (Model Context Protocol) server. Each MCP server is added as a separate connector.
|
Connect to an MCP (Model Context Protocol) server. Each MCP server is added as a separate
|
||||||
</AlertDescription>
|
connector.
|
||||||
</Alert>
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
|
||||||
<form id="mcp-connect-form" onSubmit={handleSubmit} className="space-y-6">
|
<form id="mcp-connect-form" onSubmit={handleSubmit} className="space-y-6">
|
||||||
<div className="rounded-xl border border-border bg-slate-400/5 dark:bg-white/5 p-4 sm:p-6 space-y-4">
|
<div className="rounded-xl border border-border bg-slate-400/5 dark:bg-white/5 p-4 sm:p-6 space-y-4">
|
||||||
|
|
@ -140,11 +141,10 @@ export const MCPConnectForm: FC<ConnectFormProps> = ({ onSubmit, isSubmitting })
|
||||||
rows={16}
|
rows={16}
|
||||||
className={`font-mono text-xs ${jsonError ? "border-red-500" : ""}`}
|
className={`font-mono text-xs ${jsonError ? "border-red-500" : ""}`}
|
||||||
/>
|
/>
|
||||||
{jsonError && (
|
{jsonError && <p className="text-xs text-red-500">JSON Error: {jsonError}</p>}
|
||||||
<p className="text-xs text-red-500">JSON Error: {jsonError}</p>
|
|
||||||
)}
|
|
||||||
<p className="text-[10px] sm:text-xs text-muted-foreground">
|
<p className="text-[10px] sm:text-xs text-muted-foreground">
|
||||||
Paste a single MCP server configuration. Must include: name, command, args (optional), env (optional), transport (optional).
|
Paste a single MCP server configuration. Must include: name, command, args (optional),
|
||||||
|
env (optional), transport (optional).
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -176,7 +176,9 @@ export const MCPConnectForm: FC<ConnectFormProps> = ({ onSubmit, isSubmitting })
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<AlertTitle className="text-sm">
|
<AlertTitle className="text-sm">
|
||||||
{testResult.status === "success" ? "Connection Successful" : "Connection Failed"}
|
{testResult.status === "success"
|
||||||
|
? "Connection Successful"
|
||||||
|
: "Connection Failed"}
|
||||||
</AlertTitle>
|
</AlertTitle>
|
||||||
{testResult.tools.length > 0 && (
|
{testResult.tools.length > 0 && (
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -208,9 +210,7 @@ export const MCPConnectForm: FC<ConnectFormProps> = ({ onSubmit, isSubmitting })
|
||||||
{testResult.message}
|
{testResult.message}
|
||||||
{showDetails && testResult.tools.length > 0 && (
|
{showDetails && testResult.tools.length > 0 && (
|
||||||
<div className="mt-3 pt-3 border-t border-green-500/20">
|
<div className="mt-3 pt-3 border-t border-green-500/20">
|
||||||
<p className="font-semibold mb-2">
|
<p className="font-semibold mb-2">Available tools:</p>
|
||||||
Available tools:
|
|
||||||
</p>
|
|
||||||
<ul className="list-disc list-inside text-xs space-y-0.5">
|
<ul className="list-disc list-inside text-xs space-y-0.5">
|
||||||
{testResult.tools.map((tool, i) => (
|
{testResult.tools.map((tool, i) => (
|
||||||
<li key={i}>{tool.name}</li>
|
<li key={i}>{tool.name}</li>
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
import { CheckCircle2, ChevronDown, ChevronUp, Server, XCircle } from "lucide-react";
|
import { CheckCircle2, ChevronDown, ChevronUp, Server, XCircle } from "lucide-react";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { useEffect, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { EnumConnectorName } from "@/contracts/enums/connector";
|
import { EnumConnectorName } from "@/contracts/enums/connector";
|
||||||
import type { MCPServerConfig, MCPToolDefinition } from "@/contracts/types/mcp.types";
|
import type { MCPServerConfig } from "@/contracts/types/mcp.types";
|
||||||
import type { ConnectorConfigProps } from "../index";
|
import type { ConnectorConfigProps } from "../index";
|
||||||
import {
|
import {
|
||||||
parseMCPConfig,
|
parseMCPConfig,
|
||||||
|
|
@ -22,32 +22,24 @@ interface MCPConfigProps extends ConnectorConfigProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MCPConfig: FC<MCPConfigProps> = ({ connector, onConfigChange, onNameChange }) => {
|
export const MCPConfig: FC<MCPConfigProps> = ({ connector, onConfigChange, onNameChange }) => {
|
||||||
// Validate that this is an MCP connector
|
|
||||||
if (connector.connector_type !== EnumConnectorName.MCP_CONNECTOR) {
|
|
||||||
console.error(
|
|
||||||
"MCPConfig received non-MCP connector:",
|
|
||||||
connector.connector_type
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<Alert className="border-red-500/50 bg-red-500/10">
|
|
||||||
<XCircle className="h-4 w-4 text-red-600" />
|
|
||||||
<AlertTitle>Invalid Connector Type</AlertTitle>
|
|
||||||
<AlertDescription>
|
|
||||||
This component can only be used with MCP connectors.
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const [name, setName] = useState<string>("");
|
const [name, setName] = useState<string>("");
|
||||||
const [configJson, setConfigJson] = useState("");
|
const [configJson, setConfigJson] = useState("");
|
||||||
const [jsonError, setJsonError] = useState<string | null>(null);
|
const [jsonError, setJsonError] = useState<string | null>(null);
|
||||||
const [isTesting, setIsTesting] = useState(false);
|
const [isTesting, setIsTesting] = useState(false);
|
||||||
const [showDetails, setShowDetails] = useState(false);
|
const [showDetails, setShowDetails] = useState(false);
|
||||||
const [testResult, setTestResult] = useState<MCPConnectionTestResult | null>(null);
|
const [testResult, setTestResult] = useState<MCPConnectionTestResult | null>(null);
|
||||||
|
const initializedRef = useRef(false);
|
||||||
|
|
||||||
|
// Check if this is a valid MCP connector
|
||||||
|
const isValidConnector = connector.connector_type === EnumConnectorName.MCP_CONNECTOR;
|
||||||
|
|
||||||
// Initialize form from connector config (only on mount)
|
// Initialize form from connector config (only on mount)
|
||||||
|
// We intentionally only read connector.name and connector.config on initial mount
|
||||||
|
// to preserve user edits during the session
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!isValidConnector || initializedRef.current) return;
|
||||||
|
initializedRef.current = true;
|
||||||
|
|
||||||
if (connector.name) {
|
if (connector.name) {
|
||||||
setName(connector.name);
|
setName(connector.name);
|
||||||
}
|
}
|
||||||
|
|
@ -63,17 +55,19 @@ export const MCPConfig: FC<MCPConfigProps> = ({ connector, onConfigChange, onNam
|
||||||
};
|
};
|
||||||
setConfigJson(JSON.stringify(configObj, null, 2));
|
setConfigJson(JSON.stringify(configObj, null, 2));
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [isValidConnector, connector.name, connector.config?.server_config]);
|
||||||
}, []); // Only run on mount to preserve user edits
|
|
||||||
|
|
||||||
const handleNameChange = (value: string) => {
|
const handleNameChange = useCallback(
|
||||||
setName(value);
|
(value: string) => {
|
||||||
if (onNameChange) {
|
setName(value);
|
||||||
onNameChange(value);
|
if (onNameChange) {
|
||||||
}
|
onNameChange(value);
|
||||||
};
|
}
|
||||||
|
},
|
||||||
|
[onNameChange]
|
||||||
|
);
|
||||||
|
|
||||||
const parseConfig = () => {
|
const parseConfig = useCallback(() => {
|
||||||
const result = parseMCPConfig(configJson);
|
const result = parseMCPConfig(configJson);
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
setJsonError(result.error);
|
setJsonError(result.error);
|
||||||
|
|
@ -81,25 +75,26 @@ export const MCPConfig: FC<MCPConfigProps> = ({ connector, onConfigChange, onNam
|
||||||
setJsonError(null);
|
setJsonError(null);
|
||||||
}
|
}
|
||||||
return result.config;
|
return result.config;
|
||||||
};
|
}, [configJson]);
|
||||||
|
|
||||||
const handleConfigChange = (value: string) => {
|
const handleConfigChange = useCallback(
|
||||||
setConfigJson(value);
|
(value: string) => {
|
||||||
if (jsonError) {
|
setConfigJson(value);
|
||||||
setJsonError(null);
|
setJsonError(null);
|
||||||
}
|
|
||||||
|
|
||||||
// Use shared utility for validation and parsing (with caching)
|
// Use shared utility for validation and parsing (with caching)
|
||||||
const result = parseMCPConfig(value);
|
const result = parseMCPConfig(value);
|
||||||
|
|
||||||
if (result.config && onConfigChange) {
|
if (result.config && onConfigChange) {
|
||||||
// Valid config - update parent immediately
|
// Valid config - update parent immediately
|
||||||
onConfigChange({ server_config: result.config });
|
onConfigChange({ server_config: result.config });
|
||||||
}
|
}
|
||||||
// Ignore errors while typing - only show errors when user tests or saves
|
// Ignore errors while typing - only show errors when user tests or saves
|
||||||
};
|
},
|
||||||
|
[onConfigChange]
|
||||||
|
);
|
||||||
|
|
||||||
const handleTestConnection = async () => {
|
const handleTestConnection = useCallback(async () => {
|
||||||
const serverConfig = parseConfig();
|
const serverConfig = parseConfig();
|
||||||
if (!serverConfig) {
|
if (!serverConfig) {
|
||||||
setTestResult({
|
setTestResult({
|
||||||
|
|
@ -121,7 +116,19 @@ export const MCPConfig: FC<MCPConfigProps> = ({ connector, onConfigChange, onNam
|
||||||
const result = await testMCPConnection(serverConfig);
|
const result = await testMCPConnection(serverConfig);
|
||||||
setTestResult(result);
|
setTestResult(result);
|
||||||
setIsTesting(false);
|
setIsTesting(false);
|
||||||
};
|
}, [parseConfig, jsonError, onConfigChange]);
|
||||||
|
|
||||||
|
// Validate that this is an MCP connector - must be after all hooks
|
||||||
|
if (!isValidConnector) {
|
||||||
|
console.error("MCPConfig received non-MCP connector:", connector.connector_type);
|
||||||
|
return (
|
||||||
|
<Alert className="border-red-500/50 bg-red-500/10">
|
||||||
|
<XCircle className="h-4 w-4 text-red-600" />
|
||||||
|
<AlertTitle>Invalid Connector Type</AlertTitle>
|
||||||
|
<AlertDescription>This component can only be used with MCP connectors.</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
|
@ -154,11 +161,10 @@ export const MCPConfig: FC<MCPConfigProps> = ({ connector, onConfigChange, onNam
|
||||||
rows={16}
|
rows={16}
|
||||||
className={`font-mono text-xs ${jsonError ? "border-red-500" : ""}`}
|
className={`font-mono text-xs ${jsonError ? "border-red-500" : ""}`}
|
||||||
/>
|
/>
|
||||||
{jsonError && (
|
{jsonError && <p className="text-xs text-red-500">JSON Error: {jsonError}</p>}
|
||||||
<p className="text-xs text-red-500">JSON Error: {jsonError}</p>
|
|
||||||
)}
|
|
||||||
<p className="text-[10px] sm:text-xs text-muted-foreground">
|
<p className="text-[10px] sm:text-xs text-muted-foreground">
|
||||||
Edit your MCP server configuration. Must include: name, command, args (optional), env (optional), transport (optional).
|
Edit your MCP server configuration. Must include: name, command, args (optional), env
|
||||||
|
(optional), transport (optional).
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -192,7 +198,9 @@ export const MCPConfig: FC<MCPConfigProps> = ({ connector, onConfigChange, onNam
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<AlertTitle className="text-sm">
|
<AlertTitle className="text-sm">
|
||||||
{testResult.status === "success" ? "Connection Successful" : "Connection Failed"}
|
{testResult.status === "success"
|
||||||
|
? "Connection Successful"
|
||||||
|
: "Connection Failed"}
|
||||||
</AlertTitle>
|
</AlertTitle>
|
||||||
{testResult.tools.length > 0 && (
|
{testResult.tools.length > 0 && (
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -224,12 +232,10 @@ export const MCPConfig: FC<MCPConfigProps> = ({ connector, onConfigChange, onNam
|
||||||
{testResult.message}
|
{testResult.message}
|
||||||
{showDetails && testResult.tools.length > 0 && (
|
{showDetails && testResult.tools.length > 0 && (
|
||||||
<div className="mt-3 pt-3 border-t border-green-500/20">
|
<div className="mt-3 pt-3 border-t border-green-500/20">
|
||||||
<p className="font-semibold mb-2">
|
<p className="font-semibold mb-2">Available tools:</p>
|
||||||
Available tools:
|
|
||||||
</p>
|
|
||||||
<ul className="list-disc list-inside text-xs space-y-0.5">
|
<ul className="list-disc list-inside text-xs space-y-0.5">
|
||||||
{testResult.tools.map((tool, i) => (
|
{testResult.tools.map((tool) => (
|
||||||
<li key={i}>{tool.name}</li>
|
<li key={tool.name}>{tool.name}</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,10 @@ export const ConnectorConnectView: FC<ConnectorConnectViewProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl font-semibold tracking-tight">
|
<h2 className="text-xl sm:text-2xl font-semibold tracking-tight">
|
||||||
Connect {connectorType === "MCP_CONNECTOR" ? "MCP Server" : getConnectorTypeDisplay(connectorType)}
|
Connect{" "}
|
||||||
|
{connectorType === "MCP_CONNECTOR"
|
||||||
|
? "MCP Server"
|
||||||
|
: getConnectorTypeDisplay(connectorType)}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-base text-muted-foreground mt-1">
|
<p className="text-xs sm:text-base text-muted-foreground mt-1">
|
||||||
Enter your connection details
|
Enter your connection details
|
||||||
|
|
@ -139,7 +142,11 @@ export const ConnectorConnectView: FC<ConnectorConnectViewProps> = ({
|
||||||
Connecting
|
Connecting
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>{connectorType === "MCP_CONNECTOR" ? "Connect" : `Connect ${getConnectorTypeDisplay(connectorType)}`}</>
|
<>
|
||||||
|
{connectorType === "MCP_CONNECTOR"
|
||||||
|
? "Connect"
|
||||||
|
: `Connect ${getConnectorTypeDisplay(connectorType)}`}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<h2 className="text-xl sm:text-2xl font-semibold tracking-tight text-wrap whitespace-normal wrap-break-word">
|
<h2 className="text-xl sm:text-2xl font-semibold tracking-tight text-wrap whitespace-normal wrap-break-word">
|
||||||
{connector.connector_type === "MCP_CONNECTOR" ? "MCP Server" : connector.name}
|
{connector.connector_type === "MCP_CONNECTOR" ? "MCP Server" : connector.name}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-base text-muted-foreground mt-1">
|
<p className="text-xs sm:text-base text-muted-foreground mt-1">
|
||||||
Manage your connector settings and sync configuration
|
Manage your connector settings and sync configuration
|
||||||
|
|
@ -200,7 +200,6 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
|
||||||
onConfigChange={onConfigChange}
|
onConfigChange={onConfigChange}
|
||||||
onNameChange={onNameChange}
|
onNameChange={onNameChange}
|
||||||
searchSpaceId={searchSpaceId}
|
searchSpaceId={searchSpaceId}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -540,18 +540,18 @@ export const useConnectorDialog = () => {
|
||||||
data: {
|
data: {
|
||||||
...connectorData,
|
...connectorData,
|
||||||
connector_type: connectorData.connector_type as EnumConnectorName,
|
connector_type: connectorData.connector_type as EnumConnectorName,
|
||||||
is_active: true,
|
is_active: true,
|
||||||
next_scheduled_at: connectorData.next_scheduled_at as string | null,
|
next_scheduled_at: connectorData.next_scheduled_at as string | null,
|
||||||
},
|
},
|
||||||
queryParams: {
|
queryParams: {
|
||||||
search_space_id: searchSpaceId,
|
search_space_id: searchSpaceId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Refetch connectors to get the new one
|
// Refetch connectors to get the new one
|
||||||
const result = await refetchAllConnectors();
|
const result = await refetchAllConnectors();
|
||||||
if (result.data) {
|
if (result.data) {
|
||||||
const connector = result.data.find(
|
const connector = result.data.find(
|
||||||
(c: SearchSourceConnector) => c.id === newConnector.id
|
(c: SearchSourceConnector) => c.id === newConnector.id
|
||||||
);
|
);
|
||||||
if (connector) {
|
if (connector) {
|
||||||
|
|
@ -644,34 +644,35 @@ export const useConnectorDialog = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const successMessage = currentConnectorType === "MCP_CONNECTOR"
|
const successMessage =
|
||||||
? `${connector.name} MCP server added successfully`
|
currentConnectorType === "MCP_CONNECTOR"
|
||||||
: `${connectorTitle} connected and indexing started!`;
|
? `${connector.name} MCP server added successfully`
|
||||||
toast.success(successMessage, {
|
: `${connectorTitle} connected and indexing started!`;
|
||||||
description: periodicEnabledForIndexing
|
toast.success(successMessage, {
|
||||||
? `Periodic sync enabled every ${getFrequencyLabel(frequencyMinutesForIndexing)}.`
|
description: periodicEnabledForIndexing
|
||||||
: "You can continue working while we sync your data.",
|
? `Periodic sync enabled every ${getFrequencyLabel(frequencyMinutesForIndexing)}.`
|
||||||
});
|
: "You can continue working while we sync your data.",
|
||||||
|
});
|
||||||
|
|
||||||
const url = new URL(window.location.href);
|
const url = new URL(window.location.href);
|
||||||
url.searchParams.delete("modal");
|
url.searchParams.delete("modal");
|
||||||
url.searchParams.delete("tab");
|
url.searchParams.delete("tab");
|
||||||
url.searchParams.delete("view");
|
url.searchParams.delete("view");
|
||||||
url.searchParams.delete("connectorType");
|
url.searchParams.delete("connectorType");
|
||||||
router.replace(url.pathname + url.search, { scroll: false });
|
router.replace(url.pathname + url.search, { scroll: false });
|
||||||
|
|
||||||
// Clear indexing config state since we're not showing the view
|
// Clear indexing config state since we're not showing the view
|
||||||
setIndexingConfig(null);
|
setIndexingConfig(null);
|
||||||
setIndexingConnector(null);
|
setIndexingConnector(null);
|
||||||
setIndexingConnectorConfig(null);
|
setIndexingConnectorConfig(null);
|
||||||
|
|
||||||
// Invalidate queries to refresh data
|
// Invalidate queries to refresh data
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: cacheKeys.logs.summary(Number(searchSpaceId)),
|
queryKey: cacheKeys.logs.summary(Number(searchSpaceId)),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Refresh connectors list
|
// Refresh connectors list
|
||||||
await refetchAllConnectors();
|
await refetchAllConnectors();
|
||||||
} else {
|
} else {
|
||||||
// Non-indexable connector
|
// Non-indexable connector
|
||||||
// For Circleback, transition to edit view to show webhook URL
|
// For Circleback, transition to edit view to show webhook URL
|
||||||
|
|
@ -708,9 +709,10 @@ export const useConnectorDialog = () => {
|
||||||
await refetchAllConnectors();
|
await refetchAllConnectors();
|
||||||
} else {
|
} else {
|
||||||
// Other non-indexable connectors - just show success message and close
|
// Other non-indexable connectors - just show success message and close
|
||||||
const successMessage = currentConnectorType === "MCP_CONNECTOR"
|
const successMessage =
|
||||||
? `${connector.name} MCP server added successfully`
|
currentConnectorType === "MCP_CONNECTOR"
|
||||||
: `${connectorTitle} connected successfully!`;
|
? `${connector.name} MCP server added successfully`
|
||||||
|
: `${connectorTitle} connected successfully!`;
|
||||||
toast.success(successMessage);
|
toast.success(successMessage);
|
||||||
|
|
||||||
// Refresh connectors list before closing modal
|
// Refresh connectors list before closing modal
|
||||||
|
|
@ -1252,33 +1254,33 @@ export const useConnectorDialog = () => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate toast message based on connector type
|
// Generate toast message based on connector type
|
||||||
const toastTitle = `${editingConnector.name} updated successfully`;
|
const toastTitle = `${editingConnector.name} updated successfully`;
|
||||||
|
|
||||||
toast.success(toastTitle, {
|
toast.success(toastTitle, {
|
||||||
description: periodicEnabled
|
description: periodicEnabled
|
||||||
? `Periodic sync ${frequency ? `enabled every ${getFrequencyLabel(frequencyMinutes)}` : "enabled"}. ${indexingDescription}`
|
? `Periodic sync ${frequency ? `enabled every ${getFrequencyLabel(frequencyMinutes)}` : "enabled"}. ${indexingDescription}`
|
||||||
: indexingDescription,
|
: indexingDescription,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update URL - the effect will handle closing the modal and clearing state
|
// Update URL - the effect will handle closing the modal and clearing state
|
||||||
const url = new URL(window.location.href);
|
const url = new URL(window.location.href);
|
||||||
url.searchParams.delete("modal");
|
url.searchParams.delete("modal");
|
||||||
url.searchParams.delete("tab");
|
url.searchParams.delete("tab");
|
||||||
url.searchParams.delete("view");
|
url.searchParams.delete("view");
|
||||||
url.searchParams.delete("connectorId");
|
url.searchParams.delete("connectorId");
|
||||||
router.replace(url.pathname + url.search, { scroll: false });
|
router.replace(url.pathname + url.search, { scroll: false });
|
||||||
|
|
||||||
refreshConnectors();
|
refreshConnectors();
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: cacheKeys.logs.summary(Number(searchSpaceId)),
|
queryKey: cacheKeys.logs.summary(Number(searchSpaceId)),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error saving connector:", error);
|
console.error("Error saving connector:", error);
|
||||||
toast.error("Failed to save connector changes");
|
toast.error("Failed to save connector changes");
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
editingConnector,
|
editingConnector,
|
||||||
|
|
|
||||||
|
|
@ -96,9 +96,7 @@ export const ActiveConnectorsTab: FC<ActiveConnectorsTabProps> = ({
|
||||||
|
|
||||||
// Separate OAuth and non-OAuth connectors
|
// Separate OAuth and non-OAuth connectors
|
||||||
const oauthConnectors = connectors.filter((c) => oauthConnectorTypes.has(c.connector_type));
|
const oauthConnectors = connectors.filter((c) => oauthConnectorTypes.has(c.connector_type));
|
||||||
const nonOauthConnectors = connectors.filter(
|
const nonOauthConnectors = connectors.filter((c) => !oauthConnectorTypes.has(c.connector_type));
|
||||||
(c) => !oauthConnectorTypes.has(c.connector_type)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Group OAuth connectors by type
|
// Group OAuth connectors by type
|
||||||
const oauthConnectorsByType = oauthConnectors.reduce(
|
const oauthConnectorsByType = oauthConnectors.reduce(
|
||||||
|
|
@ -150,8 +148,7 @@ export const ActiveConnectorsTab: FC<ActiveConnectorsTabProps> = ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const hasActiveConnectors =
|
const hasActiveConnectors =
|
||||||
filteredOAuthConnectorTypes.length > 0 ||
|
filteredOAuthConnectorTypes.length > 0 || filteredNonOAuthConnectors.length > 0;
|
||||||
filteredNonOAuthConnectors.length > 0;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TabsContent value="active" className="m-0">
|
<TabsContent value="active" className="m-0">
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,6 @@ export const AllConnectorsTab: FC<AllConnectorsTabProps> = ({
|
||||||
isConnecting={isConnecting}
|
isConnecting={isConnecting}
|
||||||
documentCount={documentCount}
|
documentCount={documentCount}
|
||||||
accountCount={accountCount}
|
accountCount={accountCount}
|
||||||
|
|
||||||
isIndexing={isIndexing}
|
isIndexing={isIndexing}
|
||||||
onConnect={() => onConnectOAuth(connector)}
|
onConnect={() => onConnectOAuth(connector)}
|
||||||
onManage={
|
onManage={
|
||||||
|
|
@ -176,7 +175,6 @@ export const AllConnectorsTab: FC<AllConnectorsTabProps> = ({
|
||||||
isConnected={isConnected}
|
isConnected={isConnected}
|
||||||
isConnecting={isConnecting}
|
isConnecting={isConnecting}
|
||||||
documentCount={documentCount}
|
documentCount={documentCount}
|
||||||
|
|
||||||
isIndexing={isIndexing}
|
isIndexing={isIndexing}
|
||||||
onConnect={handleConnect}
|
onConnect={handleConnect}
|
||||||
onManage={
|
onManage={
|
||||||
|
|
|
||||||
|
|
@ -95,11 +95,11 @@ export const parseMCPConfig = (configJson: string): MCPConfigValidationResult =>
|
||||||
// Check cache first
|
// Check cache first
|
||||||
const cached = configCache.get(configJson);
|
const cached = configCache.get(configJson);
|
||||||
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
||||||
console.log('[MCP Validator] ✅ Using cached config');
|
console.log("[MCP Validator] ✅ Using cached config");
|
||||||
return { config: cached.config, error: null };
|
return { config: cached.config, error: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[MCP Validator] 🔍 Parsing new config...');
|
console.log("[MCP Validator] 🔍 Parsing new config...");
|
||||||
|
|
||||||
// Clean up expired cache entries periodically
|
// Clean up expired cache entries periodically
|
||||||
if (configCache.size > 100) {
|
if (configCache.size > 100) {
|
||||||
|
|
@ -111,7 +111,7 @@ export const parseMCPConfig = (configJson: string): MCPConfigValidationResult =>
|
||||||
|
|
||||||
// Validate that it's an object, not an array
|
// Validate that it's an object, not an array
|
||||||
if (Array.isArray(parsed)) {
|
if (Array.isArray(parsed)) {
|
||||||
console.error('[MCP Validator] ❌ Error: Config is an array, expected object');
|
console.error("[MCP Validator] ❌ Error: Config is an array, expected object");
|
||||||
return {
|
return {
|
||||||
config: null,
|
config: null,
|
||||||
error: "Please provide a single server configuration object, not an array",
|
error: "Please provide a single server configuration object, not an array",
|
||||||
|
|
@ -138,8 +138,8 @@ export const parseMCPConfig = (configJson: string): MCPConfigValidationResult =>
|
||||||
|
|
||||||
const formattedError = fieldPath ? `${fieldPath}: ${errorMsg}` : errorMsg;
|
const formattedError = fieldPath ? `${fieldPath}: ${errorMsg}` : errorMsg;
|
||||||
|
|
||||||
console.error('[MCP Validator] ❌ Validation error:', formattedError);
|
console.error("[MCP Validator] ❌ Validation error:", formattedError);
|
||||||
console.error('[MCP Validator] Full Zod errors:', result.error.issues);
|
console.error("[MCP Validator] Full Zod errors:", result.error.issues);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
config: null,
|
config: null,
|
||||||
|
|
@ -160,7 +160,7 @@ export const parseMCPConfig = (configJson: string): MCPConfigValidationResult =>
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('[MCP Validator] ✅ Config parsed successfully:', config);
|
console.log("[MCP Validator] ✅ Config parsed successfully:", config);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
config,
|
config,
|
||||||
|
|
@ -168,7 +168,7 @@ export const parseMCPConfig = (configJson: string): MCPConfigValidationResult =>
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMsg = error instanceof Error ? error.message : "Invalid JSON";
|
const errorMsg = error instanceof Error ? error.message : "Invalid JSON";
|
||||||
console.error('[MCP Validator] ❌ JSON parse error:', errorMsg);
|
console.error("[MCP Validator] ❌ JSON parse error:", errorMsg);
|
||||||
return {
|
return {
|
||||||
config: null,
|
config: null,
|
||||||
error: errorMsg,
|
error: errorMsg,
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,7 @@ export const MCPConnectorListView: FC<MCPConnectorListViewProps> = ({
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between mb-6 shrink-0">
|
<div className="flex items-center justify-between mb-6 shrink-0">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Button
|
<Button variant="ghost" size="icon" onClick={onBack} className="h-8 w-8">
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
onClick={onBack}
|
|
||||||
className="h-8 w-8"
|
|
||||||
>
|
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="16"
|
width="16"
|
||||||
|
|
@ -79,11 +74,7 @@ export const MCPConnectorListView: FC<MCPConnectorListViewProps> = ({
|
||||||
|
|
||||||
{/* Add New Button */}
|
{/* Add New Button */}
|
||||||
<div className="mb-4 shrink-0">
|
<div className="mb-4 shrink-0">
|
||||||
<Button
|
<Button onClick={onAddNew} className="w-full" variant="outline">
|
||||||
onClick={onAddNew}
|
|
||||||
className="w-full"
|
|
||||||
variant="outline"
|
|
||||||
>
|
|
||||||
<Plus className="h-4 w-4 mr-2" />
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
Add New MCP Server
|
Add New MCP Server
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -123,9 +114,7 @@ export const MCPConnectorListView: FC<MCPConnectorListViewProps> = ({
|
||||||
{getConnectorIcon("MCP_CONNECTOR", "size-6")}
|
{getConnectorIcon("MCP_CONNECTOR", "size-6")}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-[14px] font-semibold leading-tight truncate">
|
<p className="text-[14px] font-semibold leading-tight truncate">{serverName}</p>
|
||||||
{serverName}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,10 @@ export const ThinkingStepsDisplay: FC<{ steps: ThinkingStep[]; isThreadRunning?:
|
||||||
{/* Step dot - on top of line */}
|
{/* Step dot - on top of line */}
|
||||||
<div className="relative z-10 mt-[7px] flex shrink-0 items-center justify-center">
|
<div className="relative z-10 mt-[7px] flex shrink-0 items-center justify-center">
|
||||||
{effectiveStatus === "in_progress" ? (
|
{effectiveStatus === "in_progress" ? (
|
||||||
<span className="size-2 rounded-full bg-muted-foreground/30" />
|
<span className="relative flex size-2">
|
||||||
|
<span className="absolute inline-flex size-full animate-ping rounded-full bg-primary/60" />
|
||||||
|
<span className="relative inline-flex size-2 rounded-full bg-primary" />
|
||||||
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="size-2 rounded-full bg-muted-foreground/30" />
|
<span className="size-2 rounded-full bg-muted-foreground/30" />
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ const ThreadContent: FC<{ header?: React.ReactNode }> = ({ header }) => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ThreadPrimitive.ViewportFooter className="aui-thread-viewport-footer sticky bottom-0 mx-auto mt-auto flex w-full max-w-(--thread-max-width) flex-col gap-4 overflow-visible rounded-t-3xl bg-background pb-4 md:pb-6">
|
<ThreadPrimitive.ViewportFooter className="aui-thread-viewport-footer sticky bottom-0 z-20 mx-auto mt-auto flex w-full max-w-(--thread-max-width) flex-col gap-4 overflow-visible rounded-t-3xl bg-background pb-4 md:pb-6">
|
||||||
<ThreadScrollToBottom />
|
<ThreadScrollToBottom />
|
||||||
<AssistantIf condition={({ thread }) => !thread.isEmpty}>
|
<AssistantIf condition={({ thread }) => !thread.isEmpty}>
|
||||||
<div className="fade-in slide-in-from-bottom-4 animate-in duration-500 ease-out fill-mode-both">
|
<div className="fade-in slide-in-from-bottom-4 animate-in duration-500 ease-out fill-mode-both">
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,11 @@ import { documentTypeEnum } from "./document.types";
|
||||||
/**
|
/**
|
||||||
* Notification type enum - matches backend notification types
|
* Notification type enum - matches backend notification types
|
||||||
*/
|
*/
|
||||||
export const notificationTypeEnum = z.enum(["connector_indexing", "document_processing", "new_mention"]);
|
export const notificationTypeEnum = z.enum([
|
||||||
|
"connector_indexing",
|
||||||
|
"document_processing",
|
||||||
|
"new_mention",
|
||||||
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification status enum - used in metadata
|
* Notification status enum - used in metadata
|
||||||
|
|
|
||||||
|
|
@ -267,9 +267,13 @@ class ConnectorsApiService {
|
||||||
search_space_id: String(queryParams.search_space_id),
|
search_space_id: String(queryParams.search_space_id),
|
||||||
}).toString();
|
}).toString();
|
||||||
|
|
||||||
return baseApiService.post<MCPConnectorRead>(`/api/v1/connectors/mcp?${queryString}`, undefined, {
|
return baseApiService.post<MCPConnectorRead>(
|
||||||
body: data,
|
`/api/v1/connectors/mcp?${queryString}`,
|
||||||
});
|
undefined,
|
||||||
|
{
|
||||||
|
body: data,
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue