import React, { useMemo, useState } from "react"; import { json, type LoaderFunctionArgs, type ActionFunctionArgs, } from "@remix-run/node"; import { useLoaderData, useParams } from "@remix-run/react"; import { requireUserId, requireWorkpace } from "~/services/session.server"; import { getIntegrationDefinitions } from "~/services/integrationDefinition.server"; import { getIntegrationAccounts } from "~/services/integrationAccount.server"; import { getIcon, type IconType } from "~/components/icon-utils"; import { Checkbox } from "~/components/ui/checkbox"; import { MCPAuthSection } from "~/components/integrations/mcp-auth-section"; import { ConnectedAccountSection } from "~/components/integrations/connected-account-section"; import { IngestionRuleSection } from "~/components/integrations/ingestion-rule-section"; import { ApiKeyAuthSection } from "~/components/integrations/api-key-auth-section"; import { OAuthAuthSection } from "~/components/integrations/oauth-auth-section"; import { getIngestionRuleBySource, upsertIngestionRule, } from "~/services/ingestionRule.server"; import { Section } from "~/components/integrations/section"; import { PageHeader } from "~/components/common/page-header"; import { Check, Copy, Plus } from "lucide-react"; import { FIXED_INTEGRATIONS } from "~/components/integrations/utils"; import { IngestionRule, type IntegrationAccount, IntegrationDefinitionV2, } from "@prisma/client"; import { Input } from "~/components/ui/input"; import { Button } from "~/components/ui"; export async function loader({ request, params }: LoaderFunctionArgs) { const userId = await requireUserId(request); const workspace = await requireWorkpace(request); const { slug } = params; const [integrationDefinitions, integrationAccounts] = await Promise.all([ getIntegrationDefinitions(workspace.id), getIntegrationAccounts(userId), ]); // Combine fixed integrations with dynamic ones const allIntegrations = [...FIXED_INTEGRATIONS, ...integrationDefinitions]; const integration = allIntegrations.find( (def) => def.slug === slug || def.id === slug, ); if (!integration) { throw new Response("Integration not found", { status: 404 }); } const activeAccount = integrationAccounts.find( (acc) => acc.integrationDefinitionId === integration.id && acc.isActive, ); let ingestionRule = null; if (activeAccount) { ingestionRule = await getIngestionRuleBySource( activeAccount.id, workspace.id, ); } return json({ integration, integrationAccounts, userId, ingestionRule, }); } export async function action({ request, params }: ActionFunctionArgs) { const userId = await requireUserId(request); const workspace = await requireWorkpace(request); const { slug } = params; const formData = await request.formData(); const ingestionRuleText = formData.get("ingestionRule") as string; if (!ingestionRuleText) { return json({ error: "Ingestion rule is required" }, { status: 400 }); } const [integrationDefinitions, integrationAccounts] = await Promise.all([ getIntegrationDefinitions(workspace.id), getIntegrationAccounts(userId), ]); // Combine fixed integrations with dynamic ones const allIntegrations = [...FIXED_INTEGRATIONS, ...integrationDefinitions]; const integration = allIntegrations.find( (def) => def.slug === slug || def.id === slug, ); if (!integration) { throw new Response("Integration not found", { status: 404 }); } const activeAccount = integrationAccounts.find( (acc) => acc.integrationDefinitionId === integration.id && acc.isActive, ); if (!activeAccount) { return json( { error: "No active integration account found" }, { status: 400 }, ); } await upsertIngestionRule({ text: ingestionRuleText, source: activeAccount.id, workspaceId: workspace.id, userId, }); return json({ success: true }); } function parseSpec(spec: any) { if (!spec) return {}; if (typeof spec === "string") { try { return JSON.parse(spec); } catch { return {}; } } return spec; } function CustomIntegrationContent({ integration }: { integration: any }) { const memoryUrl = `https://core.heysol.ai/api/v1/mcp?source=${integration.slug}`; const [copied, setCopied] = useState(false); const copyToClipboard = async () => { try { await navigator.clipboard.writeText(memoryUrl); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch (err) { console.error("Failed to copy:", err); } }; const getCustomContent = () => { switch (integration.id) { case "claude": return { title: "About Claude", content: (
Claude is an AI assistant created by Anthropic. It can help with a wide variety of tasks including:
For Claude Web, Desktop, and Code - OAuth authentication handled automatically
Cursor is an AI-powered code editor that helps developers write code faster and more efficiently.
{JSON.stringify(
{
memory: {
url: memoryUrl,
},
},
null,
2,
)}
Cline is an AI coding assistant that works directly in your terminal and command line environment.
Visual Studio Code is a lightweight but powerful source code editor with extensive extension support.
You need to enable MCP in settings
{JSON.stringify(
{
"chat.mcp.enabled": true,
"chat.mcp.discovery.enabled": true,
},
null,
2,
)}
{JSON.stringify(
{
memory: {
type: "http",
url: memoryUrl,
},
},
null,
2,
)}