Skip to main content
Glama

Convex MCP server

Official
by get-convex
TeamSSO.tsx14.1 kB
import { Team } from "generatedApi"; import { Sheet } from "@ui/Sheet"; import { Callout } from "@ui/Callout"; import { Checkbox } from "@ui/Checkbox"; import { Spinner } from "@ui/Spinner"; import { Button } from "@ui/Button"; import { ConfirmationDialog } from "@ui/ConfirmationDialog"; import { useIsCurrentMemberTeamAdmin } from "api/roles"; import { useTeamEntitlements, useGetSSO, useEnableSSO, useDisableSSO, useGenerateSSOConfigurationLink, } from "api/teams"; import { useState } from "react"; import { Tooltip } from "@ui/Tooltip"; import { QuestionMarkCircledIcon, ExclamationTriangleIcon, } from "@radix-ui/react-icons"; import { cn } from "@ui/cn"; import { useProfileEmails } from "api/profile"; import Link from "next/link"; import { LoadingTransition } from "@ui/Loading"; function ManageDomainsButton({ onClick, disabled, loading, variant, }: { onClick: () => Promise<void>; disabled: boolean; loading: boolean; variant: "primary" | "neutral"; }) { return ( <Button variant={variant} className="w-fit" size="sm" loading={loading} onClick={onClick} disabled={disabled} > Manage domains </Button> ); } function ManageSSOConfigurationButton({ onClick, disabled, loading, variant, tooltip, }: { onClick: () => Promise<void>; disabled: boolean; loading: boolean; variant: "primary" | "neutral"; tooltip?: string; }) { const button = ( <Button variant={variant} className="w-fit" size="sm" loading={loading} onClick={onClick} disabled={disabled} > Manage SSO configuration </Button> ); if (tooltip) { return <Tooltip tip={tooltip}>{button}</Tooltip>; } return button; } export function TeamSSO({ team }: { team: Team }) { const hasAdminPermissions = useIsCurrentMemberTeamAdmin(); const entitlements = useTeamEntitlements(team.id); const { data: ssoOrganization, isLoading: isSSOLoading } = useGetSSO(team.id); const enableSSO = useEnableSSO(team.id); const disableSSO = useDisableSSO(team.id); const generateSSOConfigurationLink = useGenerateSSOConfigurationLink(team.id); const profileEmails = useProfileEmails(); const [isSubmitting, setIsSubmitting] = useState(false); const [isGeneratingDomainsLink, setIsGeneratingDomainsLink] = useState(false); const [isGeneratingSSOLink, setIsGeneratingSSOLink] = useState(false); const [showDisableConfirmation, setShowDisableConfirmation] = useState(false); const [disableError, setDisableError] = useState<string>(); const isGeneratingAnyLink = isGeneratingDomainsLink || isGeneratingSSOLink; const ssoEnabled = entitlements?.ssoEnabled ?? false; const isSSOConfigured = !!ssoOrganization; const domains = ssoOrganization?.domains ?? []; // Determine if any domain needs verification const hasFailedDomain = domains.some((d) => d.state === "failed"); const hasVerifiedDomain = domains.some( (d) => d.state === "verified" || d.state === "legacyVerified", ); return ( <> <h2>Single Sign-On (SSO)</h2> {entitlements && !ssoEnabled && ( <Callout variant="upsell"> SSO is not available on your plan. Upgrade your plan to use SSO. </Callout> )} {isSSOConfigured && hasFailedDomain && ( <Callout variant="error"> Domain verification failed for:{" "} {domains .filter((d) => d.state === "failed") .map((d) => d.domain) .join(", ")} . Please verify your domain(s) to continue using SSO. </Callout> )} <Sheet> <h3 className="mb-2">Configuration</h3> <p className="mb-4 text-xs text-content-secondary"> Configure Single Sign-On (SSO) for your team to enable secure authentication through your identity provider. </p> <LoadingTransition> {!isSSOLoading && ( <div className="flex flex-col"> <Tooltip tip={ !hasAdminPermissions ? "You do not have permission to change SSO settings." : !ssoEnabled ? "SSO is not available on your plan." : undefined } > <label className="ml-px flex items-center gap-2 text-sm"> <Checkbox checked={isSSOConfigured} disabled={ isSubmitting || !hasAdminPermissions || !ssoEnabled } onChange={async () => { if (isSSOConfigured) { setShowDisableConfirmation(true); } else { // Enable SSO setIsSubmitting(true); try { await enableSSO({}); } finally { setIsSubmitting(false); } } }} /> Enable SSO {isSubmitting && ( <div> <Spinner /> </div> )} </label> </Tooltip> {isSSOConfigured && ( <div className="mt-5 space-y-4"> <div className="flex flex-col gap-4"> {domains.length > 0 && ( <div className="flex flex-col gap-2"> <span className="flex items-center gap-1 text-sm font-semibold"> {domains.length === 1 ? "Current Domain" : "Current Domains"} <Tooltip tip="You may remove all domains by disable and re-enabling SSO. This will require re-configuring SSO." side="right" > <QuestionMarkCircledIcon /> </Tooltip> </span> <div className="flex flex-col gap-1"> {domains.map((d) => { const isVerified = d.state === "verified" || d.state === "legacyVerified"; const isPending = d.state === "pending"; const isError = d.state === "failed"; const tooltipText = isVerified ? "This domain has been verified and may be used with SSO" : isPending ? "This domain has not yet completed verification. Check the status by clicking 'Verify domain' below." : "An error occured verifying this domain. Check the status by clicking 'Verify domain' below."; // Check if user has a verified email for this domain const hasVerifiedEmailForDomain = profileEmails?.some((email) => { if (!email.isVerified) return false; const emailDomain = email.email .split("@")[1] ?.toLowerCase(); return emailDomain === d.domain.toLowerCase(); }); return ( <div key={d.id} className="flex items-center gap-2" > <span>{d.domain}</span> <Tooltip tip={tooltipText} side="right"> <span className={cn( "rounded-full border px-2 py-0.5 text-xs", isVerified && "bg-background-success text-content-success", isPending && "bg-background-warning text-content-warning", isError && "bg-background-error text-content-error", )} > {isVerified ? "Verified" : isPending ? "Pending" : "Error"} </span> </Tooltip> {!hasVerifiedEmailForDomain && ( <Tooltip tip={ <div className="flex flex-col gap-1"> <span> You do not have a verified email on your Convex account matching this domain. If you do not add your email, you will not be able to log in with SSO with this domain. </span> <Link href="/profile" className="text-content-link hover:underline" > You may verify an email on the profile page. </Link> </div> } side="right" > <span className="flex items-center rounded-md border bg-background-warning p-1 text-content-warning"> <ExclamationTriangleIcon className="text-content-warning" /> </span> </Tooltip> )} </div> ); })} </div> </div> )} <div className="flex gap-2"> <ManageDomainsButton variant="neutral" loading={isGeneratingDomainsLink} onClick={async () => { setIsGeneratingDomainsLink(true); try { const result = await generateSSOConfigurationLink({ intent: "domainVerification", }); if (result?.link) { window.open(result.link, "_blank"); } } finally { setIsGeneratingDomainsLink(false); } }} disabled={ isSubmitting || !hasAdminPermissions || isGeneratingAnyLink } /> <ManageSSOConfigurationButton variant="primary" loading={isGeneratingSSOLink} onClick={async () => { setIsGeneratingSSOLink(true); try { const result = await generateSSOConfigurationLink({ intent: "sso", }); if (result?.link) { window.open(result.link, "_blank"); } } finally { setIsGeneratingSSOLink(false); } }} disabled={ isSubmitting || !hasAdminPermissions || !hasVerifiedDomain || isGeneratingAnyLink } tooltip={ !hasVerifiedDomain ? "You must verify at least ene domain before managing the SSO configuration." : undefined } /> </div> </div> </div> )} </div> )} </LoadingTransition> </Sheet> {showDisableConfirmation && ( <ConfirmationDialog onClose={() => { setShowDisableConfirmation(false); setDisableError(undefined); }} onConfirm={async () => { setIsSubmitting(true); try { await disableSSO(); setShowDisableConfirmation(false); } catch (e: any) { setDisableError(e.message); throw e; } finally { setIsSubmitting(false); } }} confirmText="Disable" variant="danger" dialogTitle="Disable Single Sign-On" dialogBody="Disabling Single Sign-on will remove all configuration related to SSO. You will need to re-configure all settings to re-enable SSO." error={disableError} validationText="DISABLE SSO" /> )} </> ); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/get-convex/convex-backend'

If you have feedback or need assistance with the MCP directory API, please join our Discord server