"use client" import { $fetch } from "@lib/api" import { authClient } from "@lib/auth" import { useAuth } from "@lib/auth-context" import { generateId } from "@lib/generate-id" import { useForm } from "@tanstack/react-form" import { useMutation, useQuery } from "@tanstack/react-query" import { Button } from "@ui/components/button" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@ui/components/dialog" import { Input } from "@ui/components/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@ui/components/select" import { CopyableCell } from "@ui/copyable-cell" import { CheckIcon, CopyIcon, ExternalLink, Loader2 } from "lucide-react" import Image from "next/image" import { useEffect, useState } from "react" import { toast } from "sonner" import { z } from "zod/v4" import { analytics } from "@/lib/analytics" import { cn } from "@lib/utils" import type { Project } from "@repo/lib/types" import { motion, AnimatePresence } from "motion/react" const clients = { cursor: "Cursor", claude: "Claude Desktop", vscode: "VSCode", cline: "Cline", "gemini-cli": "Gemini CLI", "claude-code": "Claude Code", "mcp-url": "MCP URL", "roo-cline": "Roo Cline", witsy: "Witsy", enconvo: "Enconvo", } as const const mcpMigrationSchema = z.object({ url: z .string() .min(1, "MCP Link is required") .regex( /^https:\/\/mcp\.supermemory\.ai\/[^/]+\/sse$/, "Link must be in format: https://mcp.supermemory.ai/userId/sse", ), }) interface ConnectAIModalProps { children: React.ReactNode open?: boolean onOpenChange?: (open: boolean) => void openInitialClient?: "mcp-url" | null openInitialTab?: "oneClick" | "manual" | null } interface ManualMCPHelpLinkProps { onClick: () => void } function ManualMCPHelpLink({ onClick }: ManualMCPHelpLinkProps) { const [isHovered, setIsHovered] = useState(false) return ( ) } export function ConnectAIModal({ children, open, onOpenChange, openInitialClient, openInitialTab, }: ConnectAIModalProps) { const { org } = useAuth() const [selectedClient, setSelectedClient] = useState< keyof typeof clients | null >(openInitialClient || null) const [internalIsOpen, setInternalIsOpen] = useState(false) const isOpen = open !== undefined ? open : internalIsOpen const setIsOpen = onOpenChange || setInternalIsOpen const [isMigrateDialogOpen, setIsMigrateDialogOpen] = useState(false) const [selectedProject, setSelectedProject] = useState("none") const [cursorInstallTab, setCursorInstallTab] = useState< "oneClick" | "manual" >("oneClick") const [mcpUrlTab, setMcpUrlTab] = useState<"oneClick" | "manual">( openInitialTab || "oneClick", ) const [manualApiKey, setManualApiKey] = useState(null) const [isCopied, setIsCopied] = useState(false) const [projectId, setProjectId] = useState("default") useEffect(() => { if (typeof window !== "undefined") { const storedProjectId = localStorage.getItem("selectedProject") ?? "default" setProjectId(storedProjectId) } }, []) useEffect(() => { analytics.mcpViewOpened() }, []) const { data: projects = [], isLoading: isLoadingProjects } = useQuery({ queryKey: ["projects"], queryFn: async () => { const response = await $fetch("@get/projects") if (response.error) { throw new Error(response.error?.message || "Failed to load projects") } return response.data?.projects || [] }, staleTime: 30 * 1000, }) const mcpMigrationForm = useForm({ defaultValues: { url: "" }, onSubmit: async ({ value, formApi }) => { const userId = extractUserIdFromMCPUrl(value.url) if (userId) { migrateMCPMutation.mutate({ userId, projectId }) formApi.reset() } }, validators: { onChange: mcpMigrationSchema, }, }) const extractUserIdFromMCPUrl = (url: string): string | null => { const regex = /^https:\/\/mcp\.supermemory\.ai\/([^/]+)\/sse$/ const match = url.trim().match(regex) return match?.[1] || null } const migrateMCPMutation = useMutation({ mutationFn: async ({ userId, projectId, }: { userId: string projectId: string }) => { const response = await $fetch("@post/documents/migrate-mcp", { body: { userId, projectId }, }) if (response.error) { throw new Error( response.error?.message || "Failed to migrate documents", ) } return response.data }, onSuccess: (data) => { toast.success("Migration completed!", { description: `Successfully migrated ${data?.migratedCount} documents`, }) setIsMigrateDialogOpen(false) }, onError: (error) => { toast.error("Migration failed", { description: error instanceof Error ? error.message : "Unknown error", }) }, }) const createMcpApiKeyMutation = useMutation({ mutationFn: async () => { if (!org?.id) { throw new Error("Organization ID is required") } const res = await authClient.apiKey.create({ metadata: { organizationId: org?.id, type: "mcp-manual", }, name: `mcp-manual-${generateId().slice(0, 8)}`, prefix: `sm_${org?.id}_`, }) return res.key }, onSuccess: (apiKey) => { setManualApiKey(apiKey) toast.success("API key created successfully!") }, onError: (error) => { toast.error("Failed to create API key", { description: error instanceof Error ? error.message : "Unknown error", }) }, }) // biome-ignore lint/correctness/useExhaustiveDependencies(createMcpApiKeyMutation.mutate): we need to mutate the mutation useEffect(() => { if (openInitialClient) { setSelectedClient(openInitialClient as keyof typeof clients) if (openInitialTab) { setMcpUrlTab(openInitialTab) if (org?.id) { createMcpApiKeyMutation.mutate() } } } }, [openInitialClient, openInitialTab, org?.id]) function generateInstallCommand() { if (!selectedClient) return "" let command = `npx -y install-mcp@latest https://mcp.supermemory.ai/mcp --client ${selectedClient} --oauth=yes` if (selectedProject && selectedProject !== "none") { // Remove the "sm_project_" prefix from the containerTag const projectIdForCommand = selectedProject.replace(/^sm_project_/, "") command += ` --project ${projectIdForCommand}` } return command } function getCursorDeeplink() { return "cursor://anysphere.cursor-deeplink/mcp/install?name=supermemory&config=eyJ1cmwiOiJodHRwczovL2FwaS5zdXBlcm1lbW9yeS5haS9tY3AifQ%3D%3D" } const copyToClipboard = () => { const command = generateInstallCommand() navigator.clipboard.writeText(command) analytics.mcpInstallCmdCopied() toast.success("Copied to clipboard!") } return ( {children} Connect supermemory to Your AI Enable your AI assistant to create, search, and access your memories directly using the Model Context Protocol (MCP).
{/* Step 1: Client Selection */}
1

Select Your AI Client

{Object.entries(clients) .slice(0, 7) .map(([key, clientName]) => ( ))}
{/* Step 2: One-click Install for Cursor, Project Selection for others, or MCP URL */} {selectedClient && (
2

{selectedClient === "cursor" ? "Install Supermemory MCP" : selectedClient === "mcp-url" ? "MCP Server Configuration" : "Select Target Project (Optional)"}

{selectedClient && selectedClient !== "mcp-url" && ( { setSelectedClient("mcp-url") setMcpUrlTab("manual") if ( !manualApiKey && !createMcpApiKeyMutation.isPending ) { createMcpApiKeyMutation.mutate() } }} /> )}
{/* Tabs */}
{selectedClient === "cursor" ? (
{/* Tab Content */} {cursorInstallTab === "oneClick" ? (

Click the button below to automatically install and configure Supermemory in Cursor

{ analytics.mcpInstallCmdCopied() toast.success("Opening Cursor installer...") }} > Add Supermemory MCP server to Cursor
) : (

Choose a project and follow the installation steps below

)}
) : selectedClient === "mcp-url" ? (
{mcpUrlTab === "oneClick" ? (

Use this URL to quickly configure supermemory in your AI assistant

) : (

Add this configuration to your MCP settings file with authentication

{createMcpApiKeyMutation.isPending ? (
) : ( <>
															
																{`{
  "supermemory-mcp": {
    "command": "npx",
    "args": ["-y", "mcp-remote", "https://mcp.supermemory.ai/mcp"],
    "env": {},
    "headers": {
      "Authorization": "Bearer ${manualApiKey || "your-api-key-here"}"
    }
  }
}`}
															
														

The API key is included as a Bearer token in the Authorization header

)}
)}
) : (
)}
)} {/* Step 3: Command Line - Show for manual installation or non-cursor clients */} {selectedClient && selectedClient !== "mcp-url" && (selectedClient !== "cursor" || cursorInstallTab === "manual") && (
3

{selectedClient === "cursor" && cursorInstallTab === "manual" ? "Manual Installation Command" : "Installation Command"}

{selectedClient === "cursor" && cursorInstallTab === "manual" ? "Copy and run this command in your terminal for manual installation (or switch to the one-click option above)" : "Copy and run this command in your terminal to install the MCP server"}

)} {/* Blurred Command Placeholder - Only show when no client selected */} {!selectedClient && (
3

Installation Command

Select a client above to see the installation command

)}

Use this URL to configure supermemory in your AI assistant

{/* TODO: Show when connection successful or not */} {/*

What You Can Do

  • • Ask your AI to save important information as memories
  • • Search through your saved memories during conversations
  • • Get contextual information from your knowledge base
*/}
{/* Migration Dialog */} {isMigrateDialogOpen && (
Migrate from MCP v1 Migrate your MCP documents from the legacy system.
{ e.preventDefault() e.stopPropagation() mcpMigrationForm.handleSubmit() }} >
{({ state, handleChange, handleBlur }) => ( <> handleChange(e.target.value)} placeholder="https://mcp.supermemory.ai/your-user-id/sse" value={state.value} /> {state.meta.errors.length > 0 && (

{state.meta.errors.join(", ")}

)} )}

Enter your old MCP Link in the format:
https://mcp.supermemory.ai/userId/sse

)}
) }