import type { ConnectAccount, ConnectController, ConnectProvider } from "@opengeni/connect"; import { ConnectChooser } from "./connect-chooser"; import { capabilityConnectProviderId, connectionServicePresentation, isServiceConnectProvider, } from "./connection-service-presentation"; import type { CapabilityCatalogItem, ConnectionMetadata, OpenGeniClient } from "@opengeni/sdk"; import { useEffect, useRef, useState } from "react"; import { CapabilityCatalogRow } from "./capability-catalog-row"; import { ConnectionLogo } from "./connection-logo"; import { ConnectionSkeleton } from "./connection-skeleton"; import { ConnectionServiceLogo } from "./connection-service-logo"; import { McpConnectionCard } from "./components/session-mcp-capability-card"; import { mcpConnectionDiscoveryState } from "./mcp-connection-status"; import { useOpenGeniText } from "./messages/runtime"; export type ConnectionDiscoveryProps = { client: OpenGeniClient; controller?: ConnectController | undefined; workspaceId: string; returnUrl: string; onConfigured?: (() => void | Promise) | undefined; }; /** Service discovery over the native catalogue. No host-owned OAuth state or * assumed account selection: details resolve current authority before acting. */ export function ConnectionDiscovery(props: ConnectionDiscoveryProps) { const [scope, setScope] = useState({ client: props.client, controller: props.controller }); const [generation, setGeneration] = useState(0); if (scope.client !== props.client || scope.controller !== props.controller) { setScope({ client: props.client, controller: props.controller }); setGeneration(generation + 1); } return ; } function ScopedDiscovery({ client, controller, workspaceId, returnUrl, onConfigured, }: ConnectionDiscoveryProps) { const t = useOpenGeniText(); const [providers, setProviders] = useState(null); const [accounts, setAccounts] = useState([]); const [selectedProvider, setSelectedProvider] = useState(null); const [providerFailed, setProviderFailed] = useState(false); const [items, setItems] = useState(null); const [failed, setFailed] = useState(false); const [connections, setConnections] = useState([]); const [reload, setReload] = useState(0); const [query, setQuery] = useState(""); const [limit, setLimit] = useState(12); const [selected, setSelected] = useState(null); const opener = useRef(null); useEffect(() => { const abort = new AbortController(); // Capture each read independently: even a synchronously throwing transport // is captured by Promise.all, and cannot strand another read's rejection. const read = async (operation: () => Promise): Promise => { abort.signal.throwIfAborted(); return operation(); }; setFailed(false); setSelectedProvider(null); setProviders(null); setItems(null); setProviderFailed(false); if (controller) void Promise.all([ read(() => controller.transport.catalog(workspaceId, { signal: abort.signal })), read(() => controller.transport.accounts(workspaceId, { signal: abort.signal })), ]) .then(([catalog, inventory]) => { if (!abort.signal.aborted) { setProviders(catalog.filter(isServiceConnectProvider)); setAccounts(inventory); } }) .catch(() => { if (!abort.signal.aborted) setProviderFailed(true); }); void Promise.all([ read(() => client.listCapabilities(workspaceId)), read(() => client.listConnections(workspaceId)), ]) .then(([catalog, inventory]) => { if (!abort.signal.aborted) { setConnections(inventory); setItems( catalog.items.filter( (item) => item.kind === "mcp" && item.authKind === "oauth2" && Boolean(item.mcpUrl ?? item.endpointUrl) && // The provider refuses self-registration and this deployment // has no operator client for it: Connect cannot succeed. (item.enabled || item.runtime?.operatorOAuthClient?.configured !== false), ), ); } }) .catch(() => { if (!abort.signal.aborted) setFailed(true); }); return () => { abort.abort(); }; }, [client, controller, workspaceId, reload]); const matches = items?.filter((item) => `${item.name} ${item.description ?? ""} ${item.providerDomain ?? ""}` .toLocaleLowerCase() .includes(query.trim().toLocaleLowerCase()), ) ?? []; const providerMatches = (providers ?? []).filter( (provider) => !items?.some((item) => capabilityConnectProviderId(item) === provider.id) && provider.label.toLocaleLowerCase().includes(query.trim().toLocaleLowerCase()), ); const loading = (!items && !failed) || Boolean(controller && !providers && !providerFailed); if (selectedProvider && controller) return (
); return (

{t("connectionDiscovery.addAConnection")}

{(providerFailed || failed) && (

{t("connectionDiscovery.someServicesCouldNotBe")}{" "}

)}
{providerMatches.map((provider) => { const connected = accounts.some( (account) => account.providerId === provider.id && account.status === "connected", ); return ( } status={connected ? "added" : "available"} statusLabel={ connected ? t("connectionDiscovery.connected") : t("connectionDiscovery.connect") } showStatusLabel onOpen={() => setSelectedProvider(provider)} /> ); })} {matches.slice(0, limit).map((item) => { const state = mcpConnectionDiscoveryState(t, item, connections); return ( } status={state.status} statusLabel={state.label} showStatusLabel onOpen={() => { opener.current = document.activeElement instanceof HTMLElement ? document.activeElement : null; setSelected(item); }} /> ); })} {loading && ( )} {!loading && !failed && !providerFailed && !matches.length && !providerMatches.length && (

{t("connectionDiscovery.noServicesMatchYourSearch")}

)} {matches.length > limit && ( )}
{selected ? ( { setSelected(null); queueMicrotask(() => { if (opener.current?.isConnected) opener.current.focus(); }); }} onConfigured={async () => { setReload((value) => value + 1); await onConfigured?.(); }} /> ) : null}
); }