Merge pull request #1294 from CREDO23/feature/mcp-migration
Some checks are pending
Build and Push Docker Images / tag_release (push) Waiting to run
Build and Push Docker Images / build (./surfsense_backend, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_web, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_web, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (backend, surfsense-backend) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (web, surfsense-web) (push) Blocked by required conditions

[FEAT] Live connector tools via MCP OAuth and native APIs
This commit is contained in:
Rohan Verma 2026-04-22 21:00:28 -07:00 committed by GitHub
commit 7245ab4046
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
65 changed files with 4175 additions and 1463 deletions

View file

@ -8,6 +8,7 @@ import { Spinner } from "@/components/ui/spinner";
import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { cn } from "@/lib/utils";
import { LIVE_CONNECTOR_TYPES } from "../constants/connector-constants";
import { useConnectorStatus } from "../hooks/use-connector-status";
import { ConnectorStatusBadge } from "./connector-status-badge";
@ -55,6 +56,7 @@ export const ConnectorCard: FC<ConnectorCardProps> = ({
onManage,
}) => {
const isMCP = connectorType === EnumConnectorName.MCP_CONNECTOR;
const isLive = !!connectorType && LIVE_CONNECTOR_TYPES.has(connectorType);
// Get connector status
const { getConnectorStatus, isConnectorEnabled, getConnectorStatusMessage, shouldShowWarnings } =
useConnectorStatus();
@ -123,14 +125,14 @@ export const ConnectorCard: FC<ConnectorCardProps> = ({
</span>
) : (
<>
<span>{formatDocumentCount(documentCount)}</span>
{!isLive && <span>{formatDocumentCount(documentCount)}</span>}
{!isLive && accountCount !== undefined && accountCount > 0 && (
<span className="text-muted-foreground/50"></span>
)}
{accountCount !== undefined && accountCount > 0 && (
<>
<span className="text-muted-foreground/50"></span>
<span>
{accountCount} {accountCount === 1 ? "Account" : "Accounts"}
</span>
</>
<span>
{accountCount} {accountCount === 1 ? "Account" : "Accounts"}
</span>
)}
</>
)}

View file

@ -53,8 +53,7 @@ export const DiscordConfig: FC<DiscordConfigProps> = ({ connector }) => {
return () => document.removeEventListener("visibilitychange", handleVisibilityChange);
}, [connector?.id, fetchChannels]);
// Separate channels by indexing capability
const readyToIndex = channels.filter((ch) => ch.can_index);
const accessible = channels.filter((ch) => ch.can_index);
const needsPermissions = channels.filter((ch) => !ch.can_index);
// Format last fetched time
@ -80,7 +79,7 @@ export const DiscordConfig: FC<DiscordConfigProps> = ({ connector }) => {
</div>
<div className="text-xs sm:text-sm">
<p className="text-muted-foreground mt-1 text-[10px] sm:text-sm">
The bot needs &quot;Read Message History&quot; permission to index channels. Ask a
The bot needs &quot;Read Message History&quot; permission to access channels. Ask a
server admin to grant this permission for channels shown below.
</p>
</div>
@ -127,18 +126,18 @@ export const DiscordConfig: FC<DiscordConfigProps> = ({ connector }) => {
</div>
) : (
<div className="rounded-xl bg-slate-400/5 dark:bg-white/5 overflow-hidden">
{/* Ready to index */}
{readyToIndex.length > 0 && (
{/* Accessible channels */}
{accessible.length > 0 && (
<div className={cn("p-3", needsPermissions.length > 0 && "border-b border-border")}>
<div className="flex items-center gap-2 mb-2">
<CheckCircle2 className="size-3.5 text-emerald-500" />
<span className="text-[11px] font-medium">Ready to index</span>
<span className="text-[11px] font-medium">Accessible</span>
<span className="text-[10px] text-muted-foreground">
{readyToIndex.length} {readyToIndex.length === 1 ? "channel" : "channels"}
{accessible.length} {accessible.length === 1 ? "channel" : "channels"}
</span>
</div>
<div className="flex flex-wrap gap-1.5">
{readyToIndex.map((channel) => (
{accessible.map((channel) => (
<ChannelPill key={channel.id} channel={channel} />
))}
</div>
@ -150,7 +149,7 @@ export const DiscordConfig: FC<DiscordConfigProps> = ({ connector }) => {
<div className="p-3">
<div className="flex items-center gap-2 mb-2">
<AlertCircle className="size-3.5 text-amber-500" />
<span className="text-[11px] font-medium">Grant permissions to index</span>
<span className="text-[11px] font-medium">Needs permissions</span>
<span className="text-[10px] text-muted-foreground">
{needsPermissions.length}{" "}
{needsPermissions.length === 1 ? "channel" : "channels"}

View file

@ -0,0 +1,28 @@
"use client";
import { CheckCircle2 } from "lucide-react";
import type { FC } from "react";
import type { ConnectorConfigProps } from "../index";
export const MCPServiceConfig: FC<ConnectorConfigProps> = ({ connector }) => {
const serviceName = connector.config?.mcp_service as string | undefined;
const displayName = serviceName
? serviceName.charAt(0).toUpperCase() + serviceName.slice(1)
: "this service";
return (
<div className="space-y-4">
<div className="rounded-xl border border-border bg-emerald-500/5 p-4 flex items-start gap-3">
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-emerald-500/10 shrink-0 mt-0.5">
<CheckCircle2 className="size-4 text-emerald-500" />
</div>
<div className="text-xs sm:text-sm">
<p className="font-medium text-xs sm:text-sm">Connected</p>
<p className="text-muted-foreground mt-1 text-[10px] sm:text-sm">
Your agent can search, read, and take actions in {displayName}.
</p>
</div>
</div>
</div>
);
};

View file

@ -18,9 +18,9 @@ export const TeamsConfig: FC<TeamsConfigProps> = () => {
<div className="text-xs sm:text-sm">
<p className="font-medium text-xs sm:text-sm">Microsoft Teams Access</p>
<p className="text-muted-foreground mt-1 text-[10px] sm:text-sm">
SurfSense will index messages from Teams channels that you have access to. The app can
only read messages from teams and channels where you are a member. Make sure you're a
member of the teams you want to index before connecting.
Your agent can search and read messages from Teams channels you have access to,
and send messages on your behalf. Make sure you&#39;re a member of the teams
you want to interact with.
</p>
</div>
</div>

View file

@ -16,8 +16,9 @@ import { DateRangeSelector } from "../../components/date-range-selector";
import { PeriodicSyncConfig } from "../../components/periodic-sync-config";
import { SummaryConfig } from "../../components/summary-config";
import { VisionLLMConfig } from "../../components/vision-llm-config";
import { LIVE_CONNECTOR_TYPES } from "../../constants/connector-constants";
import { getConnectorDisplayName } from "../../tabs/all-connectors-tab";
import { getConnectorConfigComponent } from "../index";
import { type ConnectorConfigProps, getConnectorConfigComponent } from "../index";
const REAUTH_ENDPOINTS: Partial<Record<string, string>> = {
[EnumConnectorName.LINEAR_CONNECTOR]: "/api/v1/auth/linear/connector/reauth",
@ -118,11 +119,17 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
}
}, [searchSpaceId, searchSpaceIdAtom, reauthEndpoint, connector.id]);
// Get connector-specific config component
const ConnectorConfigComponent = useMemo(
() => getConnectorConfigComponent(connector.connector_type),
[connector.connector_type]
);
const isMCPBacked = Boolean(connector.config?.server_config);
const isLive = isMCPBacked || LIVE_CONNECTOR_TYPES.has(connector.connector_type);
// Get connector-specific config component (MCP-backed connectors use a generic view)
const ConnectorConfigComponent = useMemo(() => {
if (isMCPBacked) {
const { MCPServiceConfig } = require("../components/mcp-service-config");
return MCPServiceConfig as FC<ConnectorConfigProps>;
}
return getConnectorConfigComponent(connector.connector_type);
}, [connector.connector_type, isMCPBacked]);
const [isScrolled, setIsScrolled] = useState(false);
const [hasMoreContent, setHasMoreContent] = useState(false);
const [showDisconnectConfirm, setShowDisconnectConfirm] = useState(false);
@ -223,12 +230,14 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
{getConnectorDisplayName(connector.name)}
</h2>
<p className="text-xs sm:text-base text-muted-foreground mt-1">
Manage your connector settings and sync configuration
{isLive
? "Manage your connected account"
: "Manage your connector settings and sync configuration"}
</p>
</div>
</div>
{/* Quick Index Button - hidden when auth is expired */}
{connector.is_indexable && onQuickIndex && !isAuthExpired && (
{/* Quick Index Button - hidden for live connectors and when auth is expired */}
{connector.is_indexable && !isLive && onQuickIndex && !isAuthExpired && (
<Button
variant="secondary"
size="sm"
@ -271,8 +280,8 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
/>
)}
{/* Summary and sync settings - only shown for indexable connectors */}
{connector.is_indexable && (
{/* Summary and sync settings - hidden for live connectors */}
{connector.is_indexable && !isLive && (
<>
{/* AI Summary toggle */}
<SummaryConfig enabled={enableSummary} onEnabledChange={onEnableSummaryChange} />
@ -343,8 +352,8 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
</>
)}
{/* Info box - only shown for indexable connectors */}
{connector.is_indexable && (
{/* Info box - hidden for live connectors */}
{connector.is_indexable && !isLive && (
<div className="rounded-xl border border-border bg-primary/5 p-4 flex items-start gap-3">
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-primary/10 shrink-0 mt-0.5">
<Info className="size-4" />
@ -374,10 +383,12 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
{/* Fixed Footer - Action buttons */}
<div className="flex-shrink-0 flex flex-col sm:flex-row items-stretch sm:items-center justify-between gap-3 sm:gap-0 px-6 sm:px-12 py-6 sm:py-6 bg-muted border-t border-border">
{showDisconnectConfirm ? (
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-3 flex-1 sm:flex-initial">
{showDisconnectConfirm ? (
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-3 flex-1 sm:flex-initial">
<span className="text-xs sm:text-sm text-muted-foreground sm:whitespace-nowrap">
Are you sure?
{isLive
? "Your agent will lose access to this service."
: "This will remove all indexed data."}
</span>
<div className="flex items-center gap-2 sm:gap-3">
<Button
@ -421,7 +432,7 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
<RefreshCw className={cn("size-3.5", reauthing && "animate-spin")} />
Re-authenticate
</Button>
) : (
) : !isLive ? (
<Button
onClick={onSave}
disabled={isSaving || isDisconnecting}
@ -430,7 +441,7 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
<span className={isSaving ? "opacity-0" : ""}>Save Changes</span>
{isSaving && <Spinner size="sm" className="absolute" />}
</Button>
)}
) : null}
</div>
</div>
);

View file

@ -11,7 +11,7 @@ import { DateRangeSelector } from "../../components/date-range-selector";
import { PeriodicSyncConfig } from "../../components/periodic-sync-config";
import { SummaryConfig } from "../../components/summary-config";
import { VisionLLMConfig } from "../../components/vision-llm-config";
import type { IndexingConfigState } from "../../constants/connector-constants";
import { LIVE_CONNECTOR_TYPES, type IndexingConfigState } from "../../constants/connector-constants";
import { getConnectorDisplayName } from "../../tabs/all-connectors-tab";
import { getConnectorConfigComponent } from "../index";
@ -58,6 +58,8 @@ export const IndexingConfigurationView: FC<IndexingConfigurationViewProps> = ({
onStartIndexing,
onSkip,
}) => {
const isLive = LIVE_CONNECTOR_TYPES.has(config.connectorType);
// Get connector-specific config component
const ConnectorConfigComponent = useMemo(
() => (connector ? getConnectorConfigComponent(connector.connector_type) : null),
@ -138,7 +140,9 @@ export const IndexingConfigurationView: FC<IndexingConfigurationViewProps> = ({
)}
</div>
<p className="text-xs sm:text-base text-muted-foreground mt-1">
Configure when to start syncing your data
{isLive
? "Your account is ready to use"
: "Configure when to start syncing your data"}
</p>
</div>
</div>
@ -157,8 +161,8 @@ export const IndexingConfigurationView: FC<IndexingConfigurationViewProps> = ({
<ConnectorConfigComponent connector={connector} onConfigChange={onConfigChange} />
)}
{/* Summary and sync settings - only shown for indexable connectors */}
{connector?.is_indexable && (
{/* Summary and sync settings - hidden for live connectors */}
{connector?.is_indexable && !isLive && (
<>
{/* AI Summary toggle */}
<SummaryConfig enabled={enableSummary} onEnabledChange={onEnableSummaryChange} />
@ -209,8 +213,8 @@ export const IndexingConfigurationView: FC<IndexingConfigurationViewProps> = ({
</>
)}
{/* Info box - only shown for indexable connectors */}
{connector?.is_indexable && (
{/* Info box - hidden for live connectors */}
{connector?.is_indexable && !isLive && (
<div className="rounded-xl border border-border bg-primary/5 p-4 flex items-start gap-3">
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-primary/10 shrink-0 mt-0.5">
<Info className="size-4" />
@ -238,14 +242,20 @@ export const IndexingConfigurationView: FC<IndexingConfigurationViewProps> = ({
{/* Fixed Footer - Action buttons */}
<div className="flex-shrink-0 flex items-center justify-end px-6 sm:px-12 py-6 bg-muted">
<Button
onClick={onStartIndexing}
disabled={isStartingIndexing}
className="text-xs sm:text-sm relative"
>
<span className={isStartingIndexing ? "opacity-0" : ""}>Start Indexing</span>
{isStartingIndexing && <Spinner size="sm" className="absolute" />}
</Button>
{isLive ? (
<Button onClick={onSkip} className="text-xs sm:text-sm">
Done
</Button>
) : (
<Button
onClick={onStartIndexing}
disabled={isStartingIndexing}
className="text-xs sm:text-sm relative"
>
<span className={isStartingIndexing ? "opacity-0" : ""}>Start Indexing</span>
{isStartingIndexing && <Spinner size="sm" className="absolute" />}
</Button>
)}
</div>
</div>
);

View file

@ -1,5 +1,24 @@
import { EnumConnectorName } from "@/contracts/enums/connector";
/**
* Connectors that operate in real time (no background indexing).
* Used to adjust UI: hide sync controls, show "Connected" instead of doc counts.
*/
export const LIVE_CONNECTOR_TYPES = new Set<string>([
EnumConnectorName.LINEAR_CONNECTOR,
EnumConnectorName.SLACK_CONNECTOR,
EnumConnectorName.JIRA_CONNECTOR,
EnumConnectorName.CLICKUP_CONNECTOR,
EnumConnectorName.AIRTABLE_CONNECTOR,
EnumConnectorName.DISCORD_CONNECTOR,
EnumConnectorName.TEAMS_CONNECTOR,
EnumConnectorName.GOOGLE_CALENDAR_CONNECTOR,
EnumConnectorName.COMPOSIO_GOOGLE_CALENDAR_CONNECTOR,
EnumConnectorName.GOOGLE_GMAIL_CONNECTOR,
EnumConnectorName.COMPOSIO_GMAIL_CONNECTOR,
EnumConnectorName.LUMA_CONNECTOR,
]);
// OAuth Connectors (Quick Connect)
export const OAUTH_CONNECTORS = [
{
@ -13,7 +32,7 @@ export const OAUTH_CONNECTORS = [
{
id: "google-gmail-connector",
title: "Gmail",
description: "Search through your emails",
description: "Search, read, draft, and send emails",
connectorType: EnumConnectorName.GOOGLE_GMAIL_CONNECTOR,
authEndpoint: "/api/v1/auth/google/gmail/connector/add/",
selfHostedOnly: true,
@ -21,7 +40,7 @@ export const OAUTH_CONNECTORS = [
{
id: "google-calendar-connector",
title: "Google Calendar",
description: "Search through your events",
description: "Search and manage your events",
connectorType: EnumConnectorName.GOOGLE_CALENDAR_CONNECTOR,
authEndpoint: "/api/v1/auth/google/calendar/connector/add/",
selfHostedOnly: true,
@ -29,35 +48,35 @@ export const OAUTH_CONNECTORS = [
{
id: "airtable-connector",
title: "Airtable",
description: "Search your Airtable bases",
description: "Browse bases, tables, and records",
connectorType: EnumConnectorName.AIRTABLE_CONNECTOR,
authEndpoint: "/api/v1/auth/airtable/connector/add/",
authEndpoint: "/api/v1/auth/mcp/airtable/connector/add/",
},
{
id: "notion-connector",
title: "Notion",
description: "Search your Notion pages",
connectorType: EnumConnectorName.NOTION_CONNECTOR,
authEndpoint: "/api/v1/auth/notion/connector/add/",
authEndpoint: "/api/v1/auth/notion/connector/add",
},
{
id: "linear-connector",
title: "Linear",
description: "Search issues & projects",
description: "Search, read, and manage issues & projects",
connectorType: EnumConnectorName.LINEAR_CONNECTOR,
authEndpoint: "/api/v1/auth/linear/connector/add/",
authEndpoint: "/api/v1/auth/mcp/linear/connector/add/",
},
{
id: "slack-connector",
title: "Slack",
description: "Search Slack messages",
description: "Search and read channels and threads",
connectorType: EnumConnectorName.SLACK_CONNECTOR,
authEndpoint: "/api/v1/auth/slack/connector/add/",
authEndpoint: "/api/v1/auth/mcp/slack/connector/add/",
},
{
id: "teams-connector",
title: "Microsoft Teams",
description: "Search Teams messages",
description: "Search, read, and send messages",
connectorType: EnumConnectorName.TEAMS_CONNECTOR,
authEndpoint: "/api/v1/auth/teams/connector/add/",
},
@ -78,16 +97,16 @@ export const OAUTH_CONNECTORS = [
{
id: "discord-connector",
title: "Discord",
description: "Search Discord messages",
description: "Search, read, and send messages",
connectorType: EnumConnectorName.DISCORD_CONNECTOR,
authEndpoint: "/api/v1/auth/discord/connector/add/",
},
{
id: "jira-connector",
title: "Jira",
description: "Search Jira issues",
description: "Search, read, and manage issues",
connectorType: EnumConnectorName.JIRA_CONNECTOR,
authEndpoint: "/api/v1/auth/jira/connector/add/",
authEndpoint: "/api/v1/auth/mcp/jira/connector/add/",
},
{
id: "confluence-connector",
@ -99,9 +118,9 @@ export const OAUTH_CONNECTORS = [
{
id: "clickup-connector",
title: "ClickUp",
description: "Search ClickUp tasks",
description: "Search and read tasks",
connectorType: EnumConnectorName.CLICKUP_CONNECTOR,
authEndpoint: "/api/v1/auth/clickup/connector/add/",
authEndpoint: "/api/v1/auth/mcp/clickup/connector/add/",
},
] as const;
@ -138,7 +157,7 @@ export const OTHER_CONNECTORS = [
{
id: "luma-connector",
title: "Luma",
description: "Search Luma events",
description: "Browse, read, and create events",
connectorType: EnumConnectorName.LUMA_CONNECTOR,
},
{
@ -197,14 +216,14 @@ export const COMPOSIO_CONNECTORS = [
{
id: "composio-gmail",
title: "Gmail",
description: "Search through your emails via Composio",
description: "Search, read, draft, and send emails via Composio",
connectorType: EnumConnectorName.COMPOSIO_GMAIL_CONNECTOR,
authEndpoint: "/api/v1/auth/composio/connector/add/?toolkit_id=gmail",
},
{
id: "composio-googlecalendar",
title: "Google Calendar",
description: "Search through your events via Composio",
description: "Search and manage your events via Composio",
connectorType: EnumConnectorName.COMPOSIO_GOOGLE_CALENDAR_CONNECTOR,
authEndpoint: "/api/v1/auth/composio/connector/add/?toolkit_id=googlecalendar",
},
@ -221,14 +240,14 @@ export const COMPOSIO_TOOLKITS = [
{
id: "gmail",
name: "Gmail",
description: "Search through your emails",
isIndexable: true,
description: "Search, read, draft, and send emails",
isIndexable: false,
},
{
id: "googlecalendar",
name: "Google Calendar",
description: "Search through your events",
isIndexable: true,
description: "Search and manage your events",
isIndexable: false,
},
{
id: "slack",
@ -258,66 +277,6 @@ export interface AutoIndexConfig {
}
export const AUTO_INDEX_DEFAULTS: Record<string, AutoIndexConfig> = {
[EnumConnectorName.GOOGLE_GMAIL_CONNECTOR]: {
daysBack: 30,
daysForward: 0,
frequencyMinutes: 1440,
syncDescription: "Syncing your last 30 days of emails.",
},
[EnumConnectorName.COMPOSIO_GMAIL_CONNECTOR]: {
daysBack: 30,
daysForward: 0,
frequencyMinutes: 1440,
syncDescription: "Syncing your last 30 days of emails.",
},
[EnumConnectorName.SLACK_CONNECTOR]: {
daysBack: 30,
daysForward: 0,
frequencyMinutes: 1440,
syncDescription: "Syncing your last 30 days of messages.",
},
[EnumConnectorName.DISCORD_CONNECTOR]: {
daysBack: 30,
daysForward: 0,
frequencyMinutes: 1440,
syncDescription: "Syncing your last 30 days of messages.",
},
[EnumConnectorName.TEAMS_CONNECTOR]: {
daysBack: 30,
daysForward: 0,
frequencyMinutes: 1440,
syncDescription: "Syncing your last 30 days of messages.",
},
[EnumConnectorName.GOOGLE_CALENDAR_CONNECTOR]: {
daysBack: 90,
daysForward: 90,
frequencyMinutes: 1440,
syncDescription: "Syncing 90 days of past and upcoming events.",
},
[EnumConnectorName.COMPOSIO_GOOGLE_CALENDAR_CONNECTOR]: {
daysBack: 90,
daysForward: 90,
frequencyMinutes: 1440,
syncDescription: "Syncing 90 days of past and upcoming events.",
},
[EnumConnectorName.LINEAR_CONNECTOR]: {
daysBack: 90,
daysForward: 0,
frequencyMinutes: 1440,
syncDescription: "Syncing your last 90 days of issues.",
},
[EnumConnectorName.JIRA_CONNECTOR]: {
daysBack: 90,
daysForward: 0,
frequencyMinutes: 1440,
syncDescription: "Syncing your last 90 days of issues.",
},
[EnumConnectorName.CLICKUP_CONNECTOR]: {
daysBack: 90,
daysForward: 0,
frequencyMinutes: 1440,
syncDescription: "Syncing your last 90 days of tasks.",
},
[EnumConnectorName.NOTION_CONNECTOR]: {
daysBack: 365,
daysForward: 0,
@ -330,12 +289,6 @@ export const AUTO_INDEX_DEFAULTS: Record<string, AutoIndexConfig> = {
frequencyMinutes: 1440,
syncDescription: "Syncing your documentation.",
},
[EnumConnectorName.AIRTABLE_CONNECTOR]: {
daysBack: 365,
daysForward: 0,
frequencyMinutes: 1440,
syncDescription: "Syncing your bases.",
},
};
export const AUTO_INDEX_CONNECTOR_TYPES = new Set<string>(Object.keys(AUTO_INDEX_DEFAULTS));

View file

@ -38,6 +38,7 @@ import {
AUTO_INDEX_CONNECTOR_TYPES,
AUTO_INDEX_DEFAULTS,
COMPOSIO_CONNECTORS,
LIVE_CONNECTOR_TYPES,
OAUTH_CONNECTORS,
OTHER_CONNECTORS,
} from "../constants/connector-constants";
@ -317,7 +318,12 @@ export const useConnectorDialog = () => {
newConnector.id
);
if (
const isLiveConnector = LIVE_CONNECTOR_TYPES.has(oauthConnector.connectorType);
if (isLiveConnector) {
toast.success(`${oauthConnector.title} connected successfully!`);
await refetchAllConnectors();
} else if (
newConnector.is_indexable &&
AUTO_INDEX_CONNECTOR_TYPES.has(oauthConnector.connectorType)
) {
@ -326,6 +332,9 @@ export const useConnectorDialog = () => {
oauthConnector.title,
oauthConnector.connectorType
);
} else if (!newConnector.is_indexable) {
toast.success(`${oauthConnector.title} connected successfully!`);
await refetchAllConnectors();
} else {
toast.dismiss("auto-index");
const config = validateIndexingConfigState({

View file

@ -9,7 +9,7 @@ import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
import { getDocumentTypeLabel } from "@/lib/documents/document-type-labels";
import { cn } from "@/lib/utils";
import { COMPOSIO_CONNECTORS, OAUTH_CONNECTORS } from "../constants/connector-constants";
import { COMPOSIO_CONNECTORS, LIVE_CONNECTOR_TYPES, OAUTH_CONNECTORS } from "../constants/connector-constants";
import { getDocumentCountForConnector } from "../utils/connector-document-mapping";
import { getConnectorDisplayName } from "./all-connectors-tab";
@ -156,6 +156,7 @@ export const ActiveConnectorsTab: FC<ActiveConnectorsTabProps> = ({
{/* OAuth Connectors - Grouped by Type */}
{filteredOAuthConnectorTypes.map(([connectorType, typeConnectors]) => {
const { title } = getOAuthConnectorTypeInfo(connectorType);
const isLive = LIVE_CONNECTOR_TYPES.has(connectorType);
const isAnyIndexing = typeConnectors.some((c: SearchSourceConnector) =>
indexingConnectorIds.has(c.id)
);
@ -202,8 +203,12 @@ export const ActiveConnectorsTab: FC<ActiveConnectorsTabProps> = ({
</p>
) : (
<p className="text-[10px] text-muted-foreground mt-1 flex items-center gap-1.5">
<span>{formatDocumentCount(documentCount)}</span>
<span className="text-muted-foreground/50"></span>
{!isLive && (
<>
<span>{formatDocumentCount(documentCount)}</span>
<span className="text-muted-foreground/50"></span>
</>
)}
<span>
{accountCount} {accountCount === 1 ? "Account" : "Accounts"}
</span>
@ -230,6 +235,7 @@ export const ActiveConnectorsTab: FC<ActiveConnectorsTabProps> = ({
documentTypeCounts
);
const isMCPConnector = connector.connector_type === "MCP_CONNECTOR";
const isLive = LIVE_CONNECTOR_TYPES.has(connector.connector_type);
return (
<div
key={`connector-${connector.id}`}
@ -261,7 +267,7 @@ export const ActiveConnectorsTab: FC<ActiveConnectorsTabProps> = ({
<Spinner size="xs" />
Syncing
</p>
) : !isMCPConnector ? (
) : !isLive && !isMCPConnector ? (
<p className="text-[10px] text-muted-foreground mt-1">
{formatDocumentCount(documentCount)}
</p>

View file

@ -13,6 +13,7 @@ import type { SearchSourceConnector } from "@/contracts/types/connector.types";
import { authenticatedFetch } from "@/lib/auth-utils";
import { formatRelativeDate } from "@/lib/format-date";
import { cn } from "@/lib/utils";
import { LIVE_CONNECTOR_TYPES } from "../constants/connector-constants";
import { useConnectorStatus } from "../hooks/use-connector-status";
import { getConnectorDisplayName } from "../tabs/all-connectors-tab";
@ -43,12 +44,8 @@ interface ConnectorAccountsListViewProps {
addButtonText?: string;
}
/**
* Check if a connector type is indexable
*/
function isIndexableConnector(connectorType: string): boolean {
const nonIndexableTypes = ["MCP_CONNECTOR"];
return !nonIndexableTypes.includes(connectorType);
function isLiveConnector(connectorType: string): boolean {
return LIVE_CONNECTOR_TYPES.has(connectorType) || connectorType === "MCP_CONNECTOR";
}
export const ConnectorAccountsListView: FC<ConnectorAccountsListViewProps> = ({
@ -149,7 +146,7 @@ export const ConnectorAccountsListView: FC<ConnectorAccountsListViewProps> = ({
{connectorTitle}
</h2>
<p className="text-xs sm:text-base text-muted-foreground mt-1">
{statusMessage || "Manage your connector settings and sync configuration"}
{statusMessage || "Manage your connected accounts"}
</p>
</div>
</div>
@ -234,15 +231,13 @@ export const ConnectorAccountsListView: FC<ConnectorAccountsListViewProps> = ({
<Spinner size="xs" />
Syncing
</p>
) : (
<p className="text-[10px] text-muted-foreground mt-1 whitespace-nowrap truncate">
{isIndexableConnector(connector.connector_type)
? connector.last_indexed_at
? `Last indexed: ${formatRelativeDate(connector.last_indexed_at)}`
: "Never indexed"
: "Active"}
) : !isLiveConnector(connector.connector_type) ? (
<p className="text-[10px] mt-1 whitespace-nowrap truncate text-muted-foreground">
{connector.last_indexed_at
? `Last indexed: ${formatRelativeDate(connector.last_indexed_at)}`
: "Never indexed"}
</p>
)}
) : null}
</div>
{isAuthExpired ? (
<Button