@particle-academy/agent-integrations 0.11.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,366 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/connectors/ConnectorButtons.tsx
7
+
8
+ // src/connectors/targets.ts
9
+ var CLAUDE_CONNECTORS_URL = "https://claude.ai/settings/connectors";
10
+ function encodeBase64Json(value) {
11
+ const json = JSON.stringify(value);
12
+ if (typeof btoa === "function") {
13
+ return btoa(unescape(encodeURIComponent(json)));
14
+ }
15
+ return Buffer.from(json, "utf8").toString("base64");
16
+ }
17
+ function buildCursorDeeplink(server) {
18
+ const config = encodeBase64Json({ url: server.url });
19
+ return `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(
20
+ server.name
21
+ )}&config=${config}`;
22
+ }
23
+ function buildVscodeDeeplink(server, opts = {}) {
24
+ const scheme = opts.insiders ? "vscode-insiders" : "vscode";
25
+ const payload = encodeURIComponent(
26
+ JSON.stringify({ name: server.name, url: server.url })
27
+ );
28
+ return `${scheme}://mcp/install?${payload}`;
29
+ }
30
+ function slugifyServerName(name) {
31
+ const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
32
+ return slug || "mcp-server";
33
+ }
34
+ function buildManualConfig(server) {
35
+ return {
36
+ mcpServers: {
37
+ [slugifyServerName(server.name)]: {
38
+ command: "npx",
39
+ args: ["-y", "mcp-remote", server.url]
40
+ }
41
+ }
42
+ };
43
+ }
44
+ function buildManualConfigSnippet(server) {
45
+ return JSON.stringify(buildManualConfig(server), null, 2);
46
+ }
47
+ var CONNECTOR_TARGETS = {
48
+ "claude-web": {
49
+ id: "claude-web",
50
+ label: "Add to Claude",
51
+ mechanism: "copy-open",
52
+ hint: "Copy the MCP URL and open Claude's Connectors page \u2014 click 'Add custom connector' and paste."
53
+ },
54
+ "claude-desktop": {
55
+ id: "claude-desktop",
56
+ label: "Claude Desktop",
57
+ mechanism: "download",
58
+ hint: "Download a .mcpb bundle and double-click it to install in Claude Desktop."
59
+ },
60
+ cursor: {
61
+ id: "cursor",
62
+ label: "Add to Cursor",
63
+ mechanism: "deeplink",
64
+ hint: "Open Cursor with this MCP server pre-filled \u2014 confirm to install."
65
+ },
66
+ vscode: {
67
+ id: "vscode",
68
+ label: "Add to VS Code",
69
+ mechanism: "deeplink",
70
+ hint: "Open VS Code with this MCP server pre-filled \u2014 confirm to install."
71
+ },
72
+ manual: {
73
+ id: "manual",
74
+ label: "Manual setup",
75
+ mechanism: "snippet",
76
+ hint: "Show a config snippet to paste into any stdio MCP client."
77
+ }
78
+ };
79
+ function connectorHref(client, server, opts = {}) {
80
+ switch (client) {
81
+ case "cursor":
82
+ return buildCursorDeeplink(server);
83
+ case "vscode":
84
+ return buildVscodeDeeplink(server, opts);
85
+ default:
86
+ return null;
87
+ }
88
+ }
89
+ function ClaudeMark(props) {
90
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, ...props, children: /* @__PURE__ */ jsxRuntime.jsx("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" }) });
91
+ }
92
+ function CursorMark(props) {
93
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 3 L 20 11 L 12 13 L 9 21 Z" }) });
94
+ }
95
+ function VscodeMark(props) {
96
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, ...props, children: /* @__PURE__ */ jsxRuntime.jsx("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" }) });
97
+ }
98
+ function DesktopMark(props) {
99
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, ...props, children: /* @__PURE__ */ jsxRuntime.jsx("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" }) });
100
+ }
101
+ function WrenchMark(props) {
102
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, ...props, children: /* @__PURE__ */ jsxRuntime.jsx("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" }) });
103
+ }
104
+ var CONNECTOR_GLYPHS = {
105
+ "claude-web": ClaudeMark,
106
+ "claude-desktop": DesktopMark,
107
+ cursor: CursorMark,
108
+ vscode: VscodeMark,
109
+ manual: WrenchMark
110
+ };
111
+ var DEFAULT_CLIENTS = [
112
+ "claude-web",
113
+ "cursor",
114
+ "vscode",
115
+ "manual"
116
+ ];
117
+ function ConnectorButtons({
118
+ serverName,
119
+ mcpUrl,
120
+ clients,
121
+ mcpbDownloadUrl,
122
+ claudeConnectorsUrl = CLAUDE_CONNECTORS_URL,
123
+ vscodeInsiders,
124
+ onCopy,
125
+ onAction,
126
+ labels,
127
+ className,
128
+ style
129
+ }) {
130
+ const server = { name: serverName, url: mcpUrl };
131
+ const [copied, setCopied] = react.useState(null);
132
+ const [manualOpen, setManualOpen] = react.useState(false);
133
+ const manualId = react.useId();
134
+ const list = (clients ?? defaultClients(mcpbDownloadUrl)).filter(
135
+ (c) => c === "claude-desktop" ? !!mcpbDownloadUrl : true
136
+ );
137
+ const flashCopied = (target) => {
138
+ setCopied(target);
139
+ window.setTimeout(() => setCopied((c) => c === target ? null : c), 2e3);
140
+ };
141
+ const copy = async (value, target) => {
142
+ try {
143
+ await navigator.clipboard?.writeText(value);
144
+ flashCopied(target);
145
+ onCopy?.(target);
146
+ } catch {
147
+ }
148
+ };
149
+ const labelFor = (c) => labels?.[c] ?? CONNECTOR_TARGETS[c].label;
150
+ return /* @__PURE__ */ jsxRuntime.jsx(
151
+ "div",
152
+ {
153
+ className: ["fai-connect", className].filter(Boolean).join(" "),
154
+ style,
155
+ children: list.map((client) => {
156
+ const meta = CONNECTOR_TARGETS[client];
157
+ const Glyph = CONNECTOR_GLYPHS[client];
158
+ const base = `fai-connect__btn fai-connect__btn--${client}`;
159
+ const href = connectorHref(client, server, { insiders: vscodeInsiders });
160
+ if (href) {
161
+ return /* @__PURE__ */ jsxRuntime.jsxs(
162
+ "a",
163
+ {
164
+ href,
165
+ className: base,
166
+ title: meta.hint,
167
+ onClick: () => onAction?.(client),
168
+ children: [
169
+ /* @__PURE__ */ jsxRuntime.jsx(Glyph, { className: "fai-connect__glyph" }),
170
+ labelFor(client)
171
+ ]
172
+ },
173
+ client
174
+ );
175
+ }
176
+ if (client === "claude-desktop") {
177
+ return /* @__PURE__ */ jsxRuntime.jsxs(
178
+ "a",
179
+ {
180
+ href: mcpbDownloadUrl,
181
+ download: true,
182
+ className: base,
183
+ title: meta.hint,
184
+ onClick: () => onAction?.(client),
185
+ children: [
186
+ /* @__PURE__ */ jsxRuntime.jsx(Glyph, { className: "fai-connect__glyph" }),
187
+ labelFor(client)
188
+ ]
189
+ },
190
+ client
191
+ );
192
+ }
193
+ if (client === "claude-web") {
194
+ return /* @__PURE__ */ jsxRuntime.jsxs(
195
+ "button",
196
+ {
197
+ type: "button",
198
+ className: base,
199
+ title: meta.hint,
200
+ onClick: () => {
201
+ void copy(mcpUrl, client);
202
+ window.open(
203
+ claudeConnectorsUrl,
204
+ "_blank",
205
+ "noopener,noreferrer"
206
+ );
207
+ onAction?.(client);
208
+ },
209
+ children: [
210
+ /* @__PURE__ */ jsxRuntime.jsx(Glyph, { className: "fai-connect__glyph" }),
211
+ copied === client ? "Copied \u2014 paste in Claude" : labelFor(client)
212
+ ]
213
+ },
214
+ client
215
+ );
216
+ }
217
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fai-connect__manual-wrap", children: [
218
+ /* @__PURE__ */ jsxRuntime.jsxs(
219
+ "button",
220
+ {
221
+ type: "button",
222
+ className: base,
223
+ title: meta.hint,
224
+ "aria-expanded": manualOpen,
225
+ "aria-controls": manualId,
226
+ onClick: () => {
227
+ setManualOpen((o) => !o);
228
+ onAction?.(client);
229
+ },
230
+ children: [
231
+ /* @__PURE__ */ jsxRuntime.jsx(Glyph, { className: "fai-connect__glyph" }),
232
+ labelFor(client)
233
+ ]
234
+ }
235
+ ),
236
+ manualOpen && /* @__PURE__ */ jsxRuntime.jsx(
237
+ ManualPopover,
238
+ {
239
+ id: manualId,
240
+ snippet: buildManualConfigSnippet(server),
241
+ copied: copied === client,
242
+ onCopy: () => copy(buildManualConfigSnippet(server), client),
243
+ onClose: () => setManualOpen(false)
244
+ }
245
+ )
246
+ ] }, client);
247
+ })
248
+ }
249
+ );
250
+ }
251
+ function defaultClients(mcpbDownloadUrl) {
252
+ return mcpbDownloadUrl ? ["claude-web", "claude-desktop", "cursor", "vscode", "manual"] : DEFAULT_CLIENTS;
253
+ }
254
+ function ManualPopover({
255
+ id,
256
+ snippet,
257
+ copied,
258
+ onCopy,
259
+ onClose
260
+ }) {
261
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { id, className: "fai-connect__popover", role: "dialog", children: [
262
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fai-connect__popover-head", children: [
263
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Add to any stdio MCP client" }),
264
+ /* @__PURE__ */ jsxRuntime.jsx(
265
+ "button",
266
+ {
267
+ type: "button",
268
+ className: "fai-connect__popover-close",
269
+ "aria-label": "Close",
270
+ onClick: onClose,
271
+ children: "\xD7"
272
+ }
273
+ )
274
+ ] }),
275
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "fai-connect__popover-hint", children: [
276
+ "Paste into ",
277
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: "claude_desktop_config.json" }),
278
+ " (or any stdio MCP client config). Needs Node 18+."
279
+ ] }),
280
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "fai-connect__snippet", children: snippet }),
281
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "fai-connect__copy-btn", onClick: onCopy, children: copied ? "Copied" : "Copy snippet" })
282
+ ] });
283
+ }
284
+
285
+ // src/connectors/mcpb.ts
286
+ var MCPB_MANIFEST_VERSION = "0.2";
287
+ var MCPB_MIN_NODE = ">=18.0.0";
288
+ var DEFAULT_MCPB_ENTRY_POINT = "server/proxy.js";
289
+ function buildMcpbManifest(input) {
290
+ const entryPoint = input.entryPoint ?? DEFAULT_MCPB_ENTRY_POINT;
291
+ return {
292
+ manifest_version: MCPB_MANIFEST_VERSION,
293
+ name: input.name,
294
+ display_name: input.display_name ?? input.name,
295
+ version: input.version,
296
+ description: input.description,
297
+ ...input.long_description ? { long_description: input.long_description } : {},
298
+ author: input.author,
299
+ ...input.homepage ? { homepage: input.homepage } : {},
300
+ ...input.documentation ? { documentation: input.documentation } : {},
301
+ ...input.support ? { support: input.support } : {},
302
+ server: {
303
+ type: "node",
304
+ entry_point: entryPoint,
305
+ mcp_config: {
306
+ command: "npx",
307
+ args: ["-y", "mcp-remote", input.mcpUrl]
308
+ }
309
+ },
310
+ tools: input.tools ?? [],
311
+ tools_generated: false,
312
+ prompts_generated: false,
313
+ ...input.keywords ? { keywords: input.keywords } : {},
314
+ license: input.license ?? "MIT",
315
+ compatibility: {
316
+ claude_desktop: ">=0.10.0",
317
+ platforms: ["darwin", "win32", "linux"],
318
+ runtimes: { node: MCPB_MIN_NODE }
319
+ }
320
+ };
321
+ }
322
+ function buildMcpbProxyStub(mcpUrl) {
323
+ const urlLiteral = JSON.stringify(mcpUrl);
324
+ return `#!/usr/bin/env node
325
+ // MCPB proxy shim (generated by @particle-academy/agent-integrations).
326
+ //
327
+ // MCPB (Claude Desktop Extensions) only supports local stdio servers, but this
328
+ // MCP server is a remote HTTP endpoint. The manifest's \`mcp_config\` invokes
329
+ // \`npx -y mcp-remote <url>\` to bridge the gap \u2014 this file is the entry_point
330
+ // fallback the manifest validator requires. If you're seeing this run,
331
+ // mcp_config wasn't honored; spawn mcp-remote directly so the bundle still works.
332
+
333
+ const { spawn } = require("node:child_process");
334
+
335
+ const url = ${urlLiteral};
336
+ const child = spawn("npx", ["-y", "mcp-remote", url], { stdio: "inherit" });
337
+
338
+ child.on("exit", (code) => process.exit(code ?? 0));
339
+ process.on("SIGINT", () => child.kill("SIGINT"));
340
+ process.on("SIGTERM", () => child.kill("SIGTERM"));
341
+ `;
342
+ }
343
+
344
+ exports.CLAUDE_CONNECTORS_URL = CLAUDE_CONNECTORS_URL;
345
+ exports.CONNECTOR_GLYPHS = CONNECTOR_GLYPHS;
346
+ exports.CONNECTOR_TARGETS = CONNECTOR_TARGETS;
347
+ exports.ClaudeMark = ClaudeMark;
348
+ exports.ConnectorButtons = ConnectorButtons;
349
+ exports.CursorMark = CursorMark;
350
+ exports.DEFAULT_MCPB_ENTRY_POINT = DEFAULT_MCPB_ENTRY_POINT;
351
+ exports.DesktopMark = DesktopMark;
352
+ exports.MCPB_MANIFEST_VERSION = MCPB_MANIFEST_VERSION;
353
+ exports.MCPB_MIN_NODE = MCPB_MIN_NODE;
354
+ exports.VscodeMark = VscodeMark;
355
+ exports.WrenchMark = WrenchMark;
356
+ exports.buildCursorDeeplink = buildCursorDeeplink;
357
+ exports.buildManualConfig = buildManualConfig;
358
+ exports.buildManualConfigSnippet = buildManualConfigSnippet;
359
+ exports.buildMcpbManifest = buildMcpbManifest;
360
+ exports.buildMcpbProxyStub = buildMcpbProxyStub;
361
+ exports.buildVscodeDeeplink = buildVscodeDeeplink;
362
+ exports.connectorHref = connectorHref;
363
+ exports.encodeBase64Json = encodeBase64Json;
364
+ exports.slugifyServerName = slugifyServerName;
365
+ //# sourceMappingURL=connectors.cjs.map
366
+ //# sourceMappingURL=connectors.cjs.map
@@ -0,0 +1 @@
1
+ {"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 .replace(/[^a-z0-9]+/g, \"-\")\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"]}
@@ -0,0 +1,4 @@
1
+ export { CLAUDE_CONNECTORS_URL, CONNECTOR_GLYPHS, CONNECTOR_TARGETS, ClaudeMark, ConnectorButtons, CursorMark, DesktopMark, VscodeMark, WrenchMark, buildCursorDeeplink, buildManualConfig, buildManualConfigSnippet, buildVscodeDeeplink, connectorHref, encodeBase64Json, slugifyServerName } from './chunk-54QEFRMS.js';
2
+ export { DEFAULT_MCPB_ENTRY_POINT, MCPB_MANIFEST_VERSION, MCPB_MIN_NODE, buildMcpbManifest, buildMcpbProxyStub } from './chunk-GO2Y6H6U.js';
3
+ //# sourceMappingURL=connectors.js.map
4
+ //# sourceMappingURL=connectors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"connectors.js"}