Skip to main content
Glama

Convex MCP server

Official
by get-convex
DeploymentMenuOptions.tsx10.7 kB
import { useDefaultDevDeployment } from "api/deployments"; import { useTeamMembers, useTeamEntitlements } from "api/teams"; import { useProfile } from "api/profile"; import { useRouter } from "next/router"; import React, { useRef } from "react"; import { CommandLineIcon, SignalIcon } from "@heroicons/react/24/outline"; import { Tooltip } from "@ui/Tooltip"; import { GearIcon, GlobeIcon, Pencil2Icon, Share1Icon, } from "@radix-ui/react-icons"; import { DeploymentResponse, ProjectDetails, Team } from "generatedApi"; import { PROVISION_PROD_PAGE_NAME } from "@common/lib/deploymentContext"; import { useIsOverflowing } from "@common/lib/useIsOverflowing"; import { ContextMenu } from "@common/features/data/components/ContextMenu"; import { Key } from "@ui/KeyboardShortcut"; export function DeploymentMenuOptions({ team, project, deployments, }: { team: Team; project: ProjectDetails; deployments: DeploymentResponse[]; }) { const member = useProfile(); const router = useRouter(); const arePreviewDeploymentsAvailable = useTeamEntitlements(team.id)?.projectMaxPreviewDeployments !== 0; const previews = deployments .filter((d) => d.deploymentType === "preview") .sort((a, b) => b.createTime - a.createTime); const members = useTeamMembers(team.id); const teamMemberDeployments = deployments .filter( (d) => d.kind === "cloud" && d.deploymentType === "dev" && d.creator !== member?.id, ) .map((d) => { const whose = members?.find((tm) => tm.id === d.creator); return { name: d.name, creator: whose?.name || whose?.email || "Teammate", }; }) .sort((a, b) => a.creator.localeCompare(b.creator)); const projectSlug = project.slug; const selectedTeamSlug = team.slug; const prod = deployments?.find((d) => d.deploymentType === "prod"); const projectsURI = `/t/${selectedTeamSlug}/${projectSlug}`; // 0-4 are /t/[team]/[project]/[deploymentName]. // 5- is the currentView, without query params const currentView = router.asPath.split("?")[0].split("/").slice(5).join("/"); return ( <> <ContextMenu.Item icon={<SignalIcon className="h-4 w-4" />} label={ <DeploymentOption name={prod?.name || "Select to create a Prod deployment"} identifier="Production" /> } shortcut={["Ctrl", "Alt", "1"]} action={ prod ? `${projectsURI}/${prod.name}/${currentView}` : `${projectsURI}/${PROVISION_PROD_PAGE_NAME}` } blankTarget={false} /> <AllPersonalDeployments team={team} project={project} deployments={deployments} /> {previews.length === 0 && ( <ContextMenu.Item icon={<Pencil2Icon className="h-4 w-4" />} label={ <div className="flex flex-col"> Preview Deployments <NoPreview isDemo={project.isDemo} arePreviewDeploymentsAvailable={arePreviewDeploymentsAvailable} /> </div> } proBadge={!arePreviewDeploymentsAvailable} blankTarget={false} action="https://docs.convex.dev/production/hosting/preview-deployments" /> )} {previews.length > 0 && ( <ContextMenu.Submenu label={ <p className="flex flex-col"> Previews <span className="text-xs text-content-secondary"> {previews.length} deployment{previews.length === 1 ? "" : "s"} </span> </p> } icon={<Pencil2Icon className="h-4 w-4" />} > {previews .sort( (a, b) => a.previewIdentifier ?.toLowerCase() .localeCompare(b.previewIdentifier?.toLowerCase() ?? "") ?? 0, ) .map((previewDeployment) => ( <ContextMenu.Item key={previewDeployment.name} label={ <DeploymentOption identifier={ previewDeployment.previewIdentifier ?? previewDeployment.name } name={previewDeployment.name} /> } action={`${projectsURI}/${previewDeployment.name}/${currentView}`} blankTarget={false} /> ))} </ContextMenu.Submenu> )} <ContextMenu.Submenu disabled={teamMemberDeployments.length === 0} label={ <p className="flex flex-col"> Other Deployments {teamMemberDeployments.length === 0 ? ( <span className="text-xs text-content-secondary"> <span className="text-content-tertiary"> Team member deployments appear here </span> </span> ) : ( <span className="text-xs text-content-secondary"> {`${teamMemberDeployments.length} deployment${teamMemberDeployments.length === 1 ? "" : "s"}`} </span> )} </p> } icon={<Share1Icon />} > {teamMemberDeployments .sort((a, b) => a.creator.toLowerCase().localeCompare(b.creator.toLowerCase()), ) .map((d) => ( <ContextMenu.Item key={d.name} label={ <DeploymentOption identifier={`${d.creator}'s dev`} name={d.name} /> } action={`${projectsURI}/${d.name}/${currentView}`} blankTarget={false} /> ))} </ContextMenu.Submenu> <hr className="my-1 bg-border-transparent" /> <ContextMenu.Item icon={<GearIcon />} label={ <div className="flex flex-col"> Project Settings <span className="text-xs text-content-secondary"> Manage this project's configuration </span> </div> } shortcut={["Ctrl", "Alt", "S"]} action={`${projectsURI}/settings`} blankTarget={false} /> </> ); } function AllPersonalDeployments({ team, project, deployments, }: { project: ProjectDetails; team: Team; deployments: DeploymentResponse[]; }) { const member = useProfile(); const dev = useDefaultDevDeployment(project.id); const router = useRouter(); const projectSlug = project.slug; const selectedTeamSlug = team.slug; const projectsURI = `/t/${selectedTeamSlug}/${projectSlug}`; // 0-4 are /t/[team]/[project]/[deploymentName]. // 5- is the currentView const currentView = router.asPath.split("?")[0].split("/").slice(5).join("/"); const allDevDeployments = sortDevDeployments( deployments.filter( (d: DeploymentResponse) => d.deploymentType === "dev" && d.creator === member?.id, ), ); if (allDevDeployments.length === 0) { return ( <ContextMenu.Item icon={<CommandLineIcon className="h-4 w-4" />} tip={ <> You do not have a personal development deployment for this project yet. Run <code className="px-0.5">npx convex dev</code> to provision one. </> } tipSide="right" label={ <DeploymentOption identifier="Development" name="You don't have a dev deployment yet" /> } action={`${projectsURI}/${dev?.name}/${currentView}`} blankTarget={false} disabled /> ); } return ( <> {allDevDeployments .filter((d) => (d.kind === "local" ? d.isActive : true)) .map((d, idx) => ( <ContextMenu.Item key={d.name} icon={ d.kind === "local" ? ( <CommandLineIcon className="h-4 w-4" /> ) : ( <GlobeIcon className="h-4 w-4" /> ) } shortcut={ idx + 2 > 9 ? undefined : ["Ctrl", "Alt", (idx + 2).toString() as Key] } label={ <DeploymentOption identifier={`${d.kind === "local" ? `${d.deviceName}` : "Development (Cloud)"}`} name={d.kind === "local" ? `Port ${d.port}` : d.name} /> } action={`${projectsURI}/${d.name}/${currentView}`} blankTarget={false} /> ))} </> ); } function sortDevDeployments(deployments: DeploymentResponse[]) { return deployments.sort((a, b) => { // Sort inactive local deployments to the end if (a.kind === "local" && !a.isActive) { return 1; } if (b.kind === "local" && !b.isActive) { return -1; } // Sort local deployments before cloud deployments if (a.kind === "local" && b.kind === "cloud") { return -1; } if (a.kind === "cloud" && b.kind === "local") { return 1; } // Sort by last update time for local deployments if (a.kind === "local" && b.kind === "local") { return a.lastUpdateTime - b.lastUpdateTime; } return a.createTime - b.createTime; }); } function DeploymentOption({ identifier, name, }: { identifier: string; name: string; }) { const ref = useRef<HTMLDivElement>(null); const isOverflowing = useIsOverflowing(ref); return ( <Tooltip tip={ isOverflowing ? ( <div className="break-all">{identifier}</div> ) : undefined } side="right" wrapsButton > <p className="flex max-w-[20rem] flex-col truncate" ref={ref}> {identifier}{" "} {name && ( <span className={`text-xs text-content-secondary ${ !name.includes(" ") ? "font-mono" : "" }`} > {name} </span> )} </p> </Tooltip> ); } function NoPreview({ isDemo, arePreviewDeploymentsAvailable, }: { isDemo: boolean; arePreviewDeploymentsAvailable: boolean; }) { if (isDemo) { return ( <div className="text-xs text-content-secondary"> Create a new project to use preview deployments </div> ); } if (arePreviewDeploymentsAvailable) { return ( <div className="text-xs text-content-secondary"> Learn how to use preview deployments </div> ); } return ( <div className="text-xs text-content-secondary"> Available on the Pro plan </div> ); }

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