"use client" import { useState, useEffect } from "react" import Image from "next/image" import { useQueryState, parseAsString } from "nuqs" import { Button } from "@ui/components/button" import { MCPIcon } from "@ui/assets/icons" import { ArrowRight, Check, Copy, EyeOff, Eye } from "lucide-react" import { cn } from "@lib/utils" import { dmSans125ClassName, dmSansClassName } from "@/lib/fonts" import { toast } from "sonner" import { MCPSteps } from "@/components/mcp-modal/mcp-detail-view" import { PLUGIN_CATALOG } from "@/lib/plugin-catalog" interface Props { mcpUrl: string onContinue: () => void } const modalCardStyle = { boxShadow: "0 2.842px 14.211px 0 rgba(0, 0, 0, 0.25), 0.711px 0.711px 0.711px 0 rgba(255, 255, 255, 0.10) inset", } const inputBevelStyle = { boxShadow: "0px 1px 2px 0px rgba(0,43,87,0.1), inset 0px 0px 0px 1px rgba(43,49,67,0.08), inset 0px 1px 1px 0px rgba(0,0,0,0.08), inset 0px 2px 4px 0px rgba(0,0,0,0.02)", } type AgentCategory = "coding" | "productivity" type Agent = { key: string name: string tagline: string category: AgentCategory pluginId?: string } const AGENTS: Agent[] = [ { key: "cursor", name: "Cursor", tagline: "Persistent context across coding sessions.", category: "coding", }, { key: "claude-code", name: "Claude Code", tagline: "Memory and decisions across CLI sessions.", category: "coding", pluginId: "claude_code", }, { key: "vscode", name: "VS Code", tagline: "Inline context while you write.", category: "coding", }, { key: "cline", name: "Cline", tagline: "Agentic dev tasks with your memory.", category: "coding", }, { key: "codex", name: "Codex", tagline: "OpenAI Codex with persistent memory.", category: "coding", pluginId: "codex", }, { key: "gemini-cli", name: "Gemini CLI", tagline: "Gemini in your terminal, brain-aware.", category: "coding", }, { key: "claude", name: "Claude Desktop", tagline: "Memory across every Claude conversation.", category: "productivity", }, { key: "chatgpt", name: "ChatGPT", tagline: "Custom GPT backed by your brain.", category: "productivity", }, ] const CATEGORY_ORDER: { id: AgentCategory; label: string }[] = [ { id: "coding", label: "Coding" }, { id: "productivity", label: "Productivity" }, ] function agentIcon(agent: Agent) { if (agent.pluginId) { const plugin = PLUGIN_CATALOG[agent.pluginId] if (plugin) return plugin.icon } const file = agent.key === "claude-code" ? "claude" : agent.key return `/mcp-supported-tools/${file}.png` } export function StepIngest({ mcpUrl, onContinue }: Props) { const [activeCategory, setActiveCategory] = useState("coding") const [selectedKey, setSelectedKey] = useState("cursor") const [, setMcpClient] = useQueryState("mcpClient", parseAsString) const selectedAgent = AGENTS.find((a) => a.key === selectedKey) ?? AGENTS[0] useEffect(() => { if (selectedAgent && !selectedAgent.pluginId) { setMcpClient(selectedAgent.key) } else { setMcpClient(null) } }, [selectedAgent, setMcpClient]) const selectAgent = (agent: Agent) => { setSelectedKey(agent.key) } const filtered = AGENTS.filter((a) => a.category === activeCategory) return (

Use your brain anywhere

Now plug it into the tools you already use to write code, chat, think.

{selectedAgent?.pluginId ? ( ) : ( )}
) } function McpHero({ url }: { url: string }) { const [copied, setCopied] = useState(false) const copy = async () => { try { await navigator.clipboard.writeText(url) setCopied(true) toast.success("MCP URL copied") setTimeout(() => setCopied(false), 2000) } catch { toast.error("Could not copy") } } return (

Universal MCP URL

{url}

) } function PluginSteps({ pluginId }: { pluginId: string }) { const plugin = PLUGIN_CATALOG[pluginId] if (!plugin) return null const steps = plugin.installSteps ?? [] return (
{plugin.name}

Set up {plugin.name}

{plugin.tagline}

{plugin.docsUrl && ( Docs ↗ )}
{steps.map((step, i) => ( ))}
Your API key is minted in Settings → Integrations → Plugins. Mint it once and paste into the step above.
) } function PluginStep({ idx, step, }: { idx: number step: import("@/lib/plugin-catalog").InstallStep }) { const [revealed, setRevealed] = useState(false) const [copied, setCopied] = useState(false) const copy = async () => { if (!step.code) return try { await navigator.clipboard.writeText(step.code) setCopied(true) toast.success("Copied") setTimeout(() => setCopied(false), 1500) } catch { toast.error("Could not copy") } } return (
{idx}

{step.title} {step.optional && ( Optional )}

{step.description && (

{step.description}

)} {step.code && (
							{step.code}
						
{step.secret && ( )}
)}
) } function CategoryTabs({ value, onChange, }: { value: AgentCategory onChange: (c: AgentCategory) => void }) { const counts: Record = { coding: 0, productivity: 0, } for (const a of AGENTS) counts[a.category] += 1 return (
{CATEGORY_ORDER.map((cat) => { const isActive = value === cat.id return ( ) })}
) } function AgentRow({ agent, active, onClick, }: { agent: Agent active: boolean onClick: () => void }) { return ( ) }