{"version":3,"sources":["../src/connectors/targets.ts","../src/connectors/glyphs.tsx","../src/connectors/ConnectorButtons.tsx","../src/connectors/mcpb.ts"],"names":["jsx","useState","useId","jsxs"],"mappings":"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","file":"connectors.cjs","sourcesContent":["// Per-client MCP \"install\" affordances — the single source of truth for the\n// subtly-different way each MCP host wants a remote server handed to it.\n//\n// These are PURE, framework-agnostic builders (no React, no DOM): a host can\n// call them to render its own buttons, and <ConnectorButtons> is a thin UI\n// over them. Every quirk below is a real one rediscovered the hard way:\n//\n//   - Claude has NO install deeplink (web or Desktop). The best a button can do\n//     is copy the URL and open the Connectors page for a manual paste; a\n//     `.mcpb` bundle is the only \"double-click\" path (Desktop only).\n//   - Cursor's deeplink wants base64-encoded JSON; for an HTTP server the\n//     payload is just `{\"url\":\"...\"}` — no `type`, no `transport`.\n//   - VS Code wants URL-ENCODED JSON (not base64), a different scheme handler.\n//   - The manual path is a `claude_desktop_config.json` snippet that wraps the\n//     remote URL with `npx -y mcp-remote` (MCPB/stdio can't take an HTTP URL).\n\n/** The MCP hosts we know how to generate an install affordance for. */\nexport type ConnectorClient =\n  | \"claude-web\"\n  | \"claude-desktop\"\n  | \"cursor\"\n  | \"vscode\"\n  | \"manual\";\n\n/** A remote MCP server to generate install artifacts for. */\nexport interface ConnectorServer {\n  /** Human-readable server name, e.g. `\"Decksmith\"`. */\n  name: string;\n  /** The remote MCP endpoint, e.g. `\"https://decksmith.dev/mcp\"`. */\n  url: string;\n}\n\n/**\n * Claude's Connectors page — the manual \"Add custom connector\" flow.\n *\n * Claude exposes no install deeplink, so a button can only copy the URL and\n * open this page for a paste. Override per-app if Claude moves it (it has\n * historically lived at both `/settings/connectors` and `/customize/connectors`).\n */\nexport const CLAUDE_CONNECTORS_URL = \"https://claude.ai/settings/connectors\";\n\n/** Base64-encode a JSON value, working in both the browser and Node. */\nexport function encodeBase64Json(value: unknown): string {\n  const json = JSON.stringify(value);\n  if (typeof btoa === \"function\") {\n    // utf8-safe: collapse multibyte → latin1 before btoa. For ASCII (URLs) this\n    // is a no-op and matches a plain `btoa(JSON.stringify(...))`.\n    return btoa(unescape(encodeURIComponent(json)));\n  }\n  // Node without a global btoa.\n  return Buffer.from(json, \"utf8\").toString(\"base64\");\n}\n\n/**\n * Cursor install deeplink for a remote (HTTP) MCP server.\n *\n * `cursor://anysphere.cursor-deeplink/mcp/install?name=<name>&config=<base64>`,\n * where `config` is base64 of `{\"url\":\"<mcpUrl>\"}` — the HTTP shape, with no\n * `type`/`transport` keys (those are for the stdio examples in the docs). The\n * base64 is intentionally NOT percent-encoded, matching Cursor's own install\n * links. Docs: https://cursor.com/docs/context/mcp/install-links\n */\nexport function buildCursorDeeplink(server: ConnectorServer): string {\n  const config = encodeBase64Json({ url: server.url });\n  return `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(\n    server.name,\n  )}&config=${config}`;\n}\n\n/**\n * VS Code install deeplink for a remote (HTTP) MCP server.\n *\n * `vscode://mcp/install?<urlencoded-json>` — URL-ENCODED JSON (not base64), the\n * opposite encoding from Cursor and an easy one to mix up. The payload is\n * `{ \"name\", \"url\" }`; for VS Code Insiders pass `{ insiders: true }`.\n */\nexport function buildVscodeDeeplink(\n  server: ConnectorServer,\n  opts: { insiders?: boolean } = {},\n): string {\n  const scheme = opts.insiders ? \"vscode-insiders\" : \"vscode\";\n  const payload = encodeURIComponent(\n    JSON.stringify({ name: server.name, url: server.url }),\n  );\n  return `${scheme}://mcp/install?${payload}`;\n}\n\n/** A normalized server key for a config file (`My App` → `my-app`). */\nexport function slugifyServerName(name: string): string {\n  const slug = name\n    .toLowerCase()\n    // Collapse every run of non-alphanumerics to a single dash, so the slug can\n    // never contain two consecutive dashes…\n    .replace(/[^a-z0-9]+/g, \"-\")\n    // …which leaves at most ONE leading/trailing dash to strip. A single-char\n    // strip (no `+` quantifier) is equivalent here and avoids a\n    // polynomial-backtracking trim regex.\n    .replace(/^-|-$/g, \"\");\n  return slug || \"mcp-server\";\n}\n\n/** The `claude_desktop_config.json` object for a manual install. */\nexport interface ManualMcpConfig {\n  mcpServers: Record<string, { command: string; args: string[] }>;\n}\n\n/**\n * The `claude_desktop_config.json` (or any stdio MCP client config) entry for a\n * remote server, wrapping it with `npx -y mcp-remote <url>` — the standard\n * stdio→HTTP bridge, since stdio clients can't take an HTTP URL directly.\n * Requires Node 18+ on the user's machine.\n */\nexport function buildManualConfig(server: ConnectorServer): ManualMcpConfig {\n  return {\n    mcpServers: {\n      [slugifyServerName(server.name)]: {\n        command: \"npx\",\n        args: [\"-y\", \"mcp-remote\", server.url],\n      },\n    },\n  };\n}\n\n/** Pretty-printed JSON snippet of {@link buildManualConfig}, for a copy box. */\nexport function buildManualConfigSnippet(server: ConnectorServer): string {\n  return JSON.stringify(buildManualConfig(server), null, 2);\n}\n\n/** How a given client's button behaves — drives the default UI. */\nexport type ConnectorMechanism =\n  | \"copy-open\" // copy URL + open a web page (claude-web)\n  | \"download\" // download a .mcpb bundle (claude-desktop)\n  | \"deeplink\" // navigate to a custom-scheme URL (cursor / vscode)\n  | \"snippet\"; // reveal a copy-paste JSON snippet (manual)\n\n/** Display metadata for a client, so consumers don't redraw the marks. */\nexport interface ConnectorTargetMeta {\n  id: ConnectorClient;\n  /** Default button label. */\n  label: string;\n  mechanism: ConnectorMechanism;\n  /** One-line tooltip explaining what the button does. */\n  hint: string;\n}\n\nexport const CONNECTOR_TARGETS: Record<ConnectorClient, ConnectorTargetMeta> = {\n  \"claude-web\": {\n    id: \"claude-web\",\n    label: \"Add to Claude\",\n    mechanism: \"copy-open\",\n    hint: \"Copy the MCP URL and open Claude's Connectors page — click 'Add custom connector' and paste.\",\n  },\n  \"claude-desktop\": {\n    id: \"claude-desktop\",\n    label: \"Claude Desktop\",\n    mechanism: \"download\",\n    hint: \"Download a .mcpb bundle and double-click it to install in Claude Desktop.\",\n  },\n  cursor: {\n    id: \"cursor\",\n    label: \"Add to Cursor\",\n    mechanism: \"deeplink\",\n    hint: \"Open Cursor with this MCP server pre-filled — confirm to install.\",\n  },\n  vscode: {\n    id: \"vscode\",\n    label: \"Add to VS Code\",\n    mechanism: \"deeplink\",\n    hint: \"Open VS Code with this MCP server pre-filled — confirm to install.\",\n  },\n  manual: {\n    id: \"manual\",\n    label: \"Manual setup\",\n    mechanism: \"snippet\",\n    hint: \"Show a config snippet to paste into any stdio MCP client.\",\n  },\n};\n\n/**\n * Resolve the navigable href for a deeplink client (cursor / vscode), or null\n * for clients whose mechanism isn't a plain navigation.\n */\nexport function connectorHref(\n  client: ConnectorClient,\n  server: ConnectorServer,\n  opts: { insiders?: boolean } = {},\n): string | null {\n  switch (client) {\n    case \"cursor\":\n      return buildCursorDeeplink(server);\n    case \"vscode\":\n      return buildVscodeDeeplink(server, opts);\n    default:\n      return null;\n  }\n}\n","// Brand-ish glyph marks for each MCP host, so consumers don't have to redraw\n// them. Deliberately simple, single-path, `currentColor` SVGs — they inherit\n// the button's text color and stay crisp at 14px.\n\nimport type { SVGProps } from \"react\";\n\ntype GlyphProps = SVGProps<SVGSVGElement>;\n\nexport function ClaudeMark(props: GlyphProps) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden {...props}>\n      <path d=\"M12 2 L 14 10 L 22 12 L 14 14 L 12 22 L 10 14 L 2 12 L 10 10 Z\" />\n    </svg>\n  );\n}\n\nexport function CursorMark(props: GlyphProps) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden {...props}>\n      <path d=\"M3 3 L 20 11 L 12 13 L 9 21 Z\" />\n    </svg>\n  );\n}\n\nexport function VscodeMark(props: GlyphProps) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden {...props}>\n      <path d=\"M17 2 L 22 4.5 V 19.5 L 17 22 L 6.5 13.2 L 3 16 L 1.5 15 V 9 L 3 8 L 6.5 10.8 Z M 17 6.5 L 10 12 L 17 17.5 Z\" />\n    </svg>\n  );\n}\n\nexport function DesktopMark(props: GlyphProps) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden {...props}>\n      <path d=\"M3 4 H 21 A 1 1 0 0 1 22 5 V 16 A 1 1 0 0 1 21 17 H 14 V 19 H 16 V 21 H 8 V 19 H 10 V 17 H 3 A 1 1 0 0 1 2 16 V 5 A 1 1 0 0 1 3 4 Z\" />\n    </svg>\n  );\n}\n\nexport function WrenchMark(props: GlyphProps) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden {...props}>\n      <path d=\"M21 4 a 5 5 0 0 1 -6.5 6.5 L 6 19 l -3 -3 l 8.5 -8.5 A 5 5 0 0 1 17 1 l -2.5 2.5 l 1.5 3 l 3 1.5 Z\" />\n    </svg>\n  );\n}\n\nimport type { ComponentType } from \"react\";\nimport type { ConnectorClient } from \"./targets\";\n\n/** Glyph component for a given client. */\nexport const CONNECTOR_GLYPHS: Record<\n  ConnectorClient,\n  ComponentType<GlyphProps>\n> = {\n  \"claude-web\": ClaudeMark,\n  \"claude-desktop\": DesktopMark,\n  cursor: CursorMark,\n  vscode: VscodeMark,\n  manual: WrenchMark,\n};\n","import { type CSSProperties, type ReactNode, useId, useState } from \"react\";\nimport {\n  CLAUDE_CONNECTORS_URL,\n  CONNECTOR_TARGETS,\n  type ConnectorClient,\n  type ConnectorServer,\n  buildManualConfigSnippet,\n  connectorHref,\n} from \"./targets\";\nimport { CONNECTOR_GLYPHS } from \"./glyphs\";\n\nexport interface ConnectorButtonsProps {\n  /** Human-readable server name, e.g. `\"Decksmith\"`. */\n  serverName: string;\n  /** The remote MCP endpoint, e.g. `\"https://decksmith.dev/mcp\"`. */\n  mcpUrl: string;\n  /**\n   * Which client buttons to render, in order. Defaults to\n   * `[\"claude-web\", \"cursor\", \"vscode\", \"manual\"]` — plus `\"claude-desktop\"`\n   * when {@link mcpbDownloadUrl} is set. A `\"claude-desktop\"` entry with no\n   * `mcpbDownloadUrl` is skipped (there's nothing to download).\n   */\n  clients?: ConnectorClient[];\n  /** URL of a prebuilt `.mcpb` bundle; enables the Claude Desktop button. */\n  mcpbDownloadUrl?: string;\n  /** Override Claude's Connectors page (it has moved before). */\n  claudeConnectorsUrl?: string;\n  /** Target VS Code Insiders instead of stable. */\n  vscodeInsiders?: boolean;\n  /** Fired when a value is copied to the clipboard (URL or snippet). */\n  onCopy?: (target: ConnectorClient) => void;\n  /** Fired when any button is activated (after its side effect). */\n  onAction?: (target: ConnectorClient) => void;\n  /** Per-client label override. */\n  labels?: Partial<Record<ConnectorClient, ReactNode>>;\n  className?: string;\n  style?: CSSProperties;\n}\n\nconst DEFAULT_CLIENTS: ConnectorClient[] = [\n  \"claude-web\",\n  \"cursor\",\n  \"vscode\",\n  \"manual\",\n];\n\n/**\n * Per-client \"Add to <host>\" buttons for a remote MCP server, each with the\n * right (and subtly different) install behavior baked in — see {@link\n * ./targets}. Brand glyphs, copy/feedback states, and the manual-config popover\n * are owned here so a consumer just passes a name + URL.\n *\n * Needs the package stylesheet for its default look:\n * `import \"@particle-academy/agent-integrations/styles.css\"`.\n */\nexport function ConnectorButtons({\n  serverName,\n  mcpUrl,\n  clients,\n  mcpbDownloadUrl,\n  claudeConnectorsUrl = CLAUDE_CONNECTORS_URL,\n  vscodeInsiders,\n  onCopy,\n  onAction,\n  labels,\n  className,\n  style,\n}: ConnectorButtonsProps) {\n  const server: ConnectorServer = { name: serverName, url: mcpUrl };\n  const [copied, setCopied] = useState<ConnectorClient | null>(null);\n  const [manualOpen, setManualOpen] = useState(false);\n  const manualId = useId();\n\n  const list = (clients ?? defaultClients(mcpbDownloadUrl)).filter((c) =>\n    c === \"claude-desktop\" ? !!mcpbDownloadUrl : true,\n  );\n\n  const flashCopied = (target: ConnectorClient) => {\n    setCopied(target);\n    window.setTimeout(() => setCopied((c) => (c === target ? null : c)), 2000);\n  };\n\n  const copy = async (value: string, target: ConnectorClient) => {\n    try {\n      await navigator.clipboard?.writeText(value);\n      flashCopied(target);\n      onCopy?.(target);\n    } catch {\n      /* clipboard blocked — the popover/URL is still visible */\n    }\n  };\n\n  const labelFor = (c: ConnectorClient): ReactNode =>\n    labels?.[c] ?? CONNECTOR_TARGETS[c].label;\n\n  return (\n    <div\n      className={[\"fai-connect\", className].filter(Boolean).join(\" \")}\n      style={style}\n    >\n      {list.map((client) => {\n        const meta = CONNECTOR_TARGETS[client];\n        const Glyph = CONNECTOR_GLYPHS[client];\n        const base = `fai-connect__btn fai-connect__btn--${client}`;\n\n        // Deeplink clients (cursor / vscode) are plain navigations.\n        const href = connectorHref(client, server, { insiders: vscodeInsiders });\n        if (href) {\n          return (\n            <a\n              key={client}\n              href={href}\n              className={base}\n              title={meta.hint}\n              onClick={() => onAction?.(client)}\n            >\n              <Glyph className=\"fai-connect__glyph\" />\n              {labelFor(client)}\n            </a>\n          );\n        }\n\n        if (client === \"claude-desktop\") {\n          return (\n            <a\n              key={client}\n              href={mcpbDownloadUrl}\n              download\n              className={base}\n              title={meta.hint}\n              onClick={() => onAction?.(client)}\n            >\n              <Glyph className=\"fai-connect__glyph\" />\n              {labelFor(client)}\n            </a>\n          );\n        }\n\n        if (client === \"claude-web\") {\n          return (\n            <button\n              key={client}\n              type=\"button\"\n              className={base}\n              title={meta.hint}\n              onClick={() => {\n                void copy(mcpUrl, client);\n                window.open(\n                  claudeConnectorsUrl,\n                  \"_blank\",\n                  \"noopener,noreferrer\",\n                );\n                onAction?.(client);\n              }}\n            >\n              <Glyph className=\"fai-connect__glyph\" />\n              {copied === client ? \"Copied — paste in Claude\" : labelFor(client)}\n            </button>\n          );\n        }\n\n        // manual\n        return (\n          <div key={client} className=\"fai-connect__manual-wrap\">\n            <button\n              type=\"button\"\n              className={base}\n              title={meta.hint}\n              aria-expanded={manualOpen}\n              aria-controls={manualId}\n              onClick={() => {\n                setManualOpen((o) => !o);\n                onAction?.(client);\n              }}\n            >\n              <Glyph className=\"fai-connect__glyph\" />\n              {labelFor(client)}\n            </button>\n            {manualOpen && (\n              <ManualPopover\n                id={manualId}\n                snippet={buildManualConfigSnippet(server)}\n                copied={copied === client}\n                onCopy={() => copy(buildManualConfigSnippet(server), client)}\n                onClose={() => setManualOpen(false)}\n              />\n            )}\n          </div>\n        );\n      })}\n    </div>\n  );\n}\n\nfunction defaultClients(mcpbDownloadUrl?: string): ConnectorClient[] {\n  return mcpbDownloadUrl\n    ? [\"claude-web\", \"claude-desktop\", \"cursor\", \"vscode\", \"manual\"]\n    : DEFAULT_CLIENTS;\n}\n\nfunction ManualPopover({\n  id,\n  snippet,\n  copied,\n  onCopy,\n  onClose,\n}: {\n  id: string;\n  snippet: string;\n  copied: boolean;\n  onCopy: () => void;\n  onClose: () => void;\n}) {\n  return (\n    <div id={id} className=\"fai-connect__popover\" role=\"dialog\">\n      <div className=\"fai-connect__popover-head\">\n        <span>Add to any stdio MCP client</span>\n        <button\n          type=\"button\"\n          className=\"fai-connect__popover-close\"\n          aria-label=\"Close\"\n          onClick={onClose}\n        >\n          ×\n        </button>\n      </div>\n      <p className=\"fai-connect__popover-hint\">\n        Paste into <code>claude_desktop_config.json</code> (or any stdio MCP\n        client config). Needs Node 18+.\n      </p>\n      <pre className=\"fai-connect__snippet\">{snippet}</pre>\n      <button type=\"button\" className=\"fai-connect__copy-btn\" onClick={onCopy}>\n        {copied ? \"Copied\" : \"Copy snippet\"}\n      </button>\n    </div>\n  );\n}\n","// MCPB (Claude Desktop \"Extensions\" bundle) manifest + proxy generation.\n//\n// PURE — no filesystem, no child_process — so it's trivially testable. The\n// `writeMcpbBundle` Node helper (./build) layers fs + the official mcpb CLI on\n// top of these.\n//\n// The hard fact MCPB forces on you: it is STDIO-ONLY. The manifest's\n// `server.type` is one of `node` / `python` / `binary` / `uv` — there is no\n// `type: \"http\"`. So to bundle a REMOTE MCP server (what fancy-* apps almost\n// always are) you ship a thin `node` server whose `mcp_config` runs\n// `npx -y mcp-remote <url>`, bridging stdio→HTTP. The manifest validator still\n// requires `entry_point`, so an (otherwise unused) proxy stub is emitted too.\n//\n// Future-proof: when MCPB grows a real `type: \"http\"`, drop the proxy and point\n// straight at the URL — the call site here doesn't change.\n\n/** The MCPB manifest schema version this helper emits. */\nexport const MCPB_MANIFEST_VERSION = \"0.2\";\n\n/** Minimum Node the mcp-remote proxy needs on the user's machine. */\nexport const MCPB_MIN_NODE = \">=18.0.0\";\n\n/** A tool advertised in the bundle manifest (display-only metadata). */\nexport interface McpbTool {\n  name: string;\n  description?: string;\n}\n\n/** Inputs for {@link buildMcpbManifest} / `writeMcpbBundle`. */\nexport interface McpbManifestInput {\n  /** Machine name, e.g. `\"decksmith\"` (lowercase, no spaces). */\n  name: string;\n  /** Human display name, e.g. `\"Decksmith\"`. Defaults to `name`. */\n  display_name?: string;\n  /** Bundle version, e.g. `\"0.2.0\"`. */\n  version: string;\n  /** Short one-line description. */\n  description: string;\n  /** Optional longer description shown on the extension's detail view. */\n  long_description?: string;\n  author: { name: string; url?: string; email?: string };\n  homepage?: string;\n  documentation?: string;\n  support?: string;\n  /** The remote MCP endpoint the bundle proxies to. */\n  mcpUrl: string;\n  /** Advertised tools (display metadata only). */\n  tools?: McpbTool[];\n  keywords?: string[];\n  license?: string;\n  /** Entry-point path inside the bundle. Defaults to `\"server/proxy.js\"`. */\n  entryPoint?: string;\n}\n\n/** The default entry-point path the proxy stub is written to. */\nexport const DEFAULT_MCPB_ENTRY_POINT = \"server/proxy.js\";\n\n/**\n * Build the full MCPB `manifest.json` object for a remote MCP server, wrapping\n * it with `npx -y mcp-remote <url>` (stdio→HTTP). Returns a plain object ready\n * to `JSON.stringify`.\n */\nexport function buildMcpbManifest(\n  input: McpbManifestInput,\n): Record<string, unknown> {\n  const entryPoint = input.entryPoint ?? DEFAULT_MCPB_ENTRY_POINT;\n  return {\n    manifest_version: MCPB_MANIFEST_VERSION,\n    name: input.name,\n    display_name: input.display_name ?? input.name,\n    version: input.version,\n    description: input.description,\n    ...(input.long_description\n      ? { long_description: input.long_description }\n      : {}),\n    author: input.author,\n    ...(input.homepage ? { homepage: input.homepage } : {}),\n    ...(input.documentation ? { documentation: input.documentation } : {}),\n    ...(input.support ? { support: input.support } : {}),\n    server: {\n      type: \"node\",\n      entry_point: entryPoint,\n      mcp_config: {\n        command: \"npx\",\n        args: [\"-y\", \"mcp-remote\", input.mcpUrl],\n      },\n    },\n    tools: input.tools ?? [],\n    tools_generated: false,\n    prompts_generated: false,\n    ...(input.keywords ? { keywords: input.keywords } : {}),\n    license: input.license ?? \"MIT\",\n    compatibility: {\n      claude_desktop: \">=0.10.0\",\n      platforms: [\"darwin\", \"win32\", \"linux\"],\n      runtimes: { node: MCPB_MIN_NODE },\n    },\n  };\n}\n\n/**\n * The `server/proxy.js` stub. MCPB requires an `entry_point` file even though\n * `mcp_config.command` overrides it; if it ever DOES run, it spawns\n * `npx -y mcp-remote <url>` itself so the bundle still works.\n */\nexport function buildMcpbProxyStub(mcpUrl: string): string {\n  // JSON.stringify gives us a safely-quoted JS string literal for the URL.\n  const urlLiteral = JSON.stringify(mcpUrl);\n  return `#!/usr/bin/env node\n// MCPB proxy shim (generated by @particle-academy/agent-integrations).\n//\n// MCPB (Claude Desktop Extensions) only supports local stdio servers, but this\n// MCP server is a remote HTTP endpoint. The manifest's \\`mcp_config\\` invokes\n// \\`npx -y mcp-remote <url>\\` to bridge the gap — this file is the entry_point\n// fallback the manifest validator requires. If you're seeing this run,\n// mcp_config wasn't honored; spawn mcp-remote directly so the bundle still works.\n\nconst { spawn } = require(\"node:child_process\");\n\nconst url = ${urlLiteral};\nconst child = spawn(\"npx\", [\"-y\", \"mcp-remote\", url], { stdio: \"inherit\" });\n\nchild.on(\"exit\", (code) => process.exit(code ?? 0));\nprocess.on(\"SIGINT\", () => child.kill(\"SIGINT\"));\nprocess.on(\"SIGTERM\", () => child.kill(\"SIGTERM\"));\n`;\n}\n"]}