@particle-academy/agent-integrations 0.20.0 → 0.21.1
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.
- package/dist/bridges/catalog.d.cts +150 -0
- package/dist/bridges/catalog.d.ts +150 -0
- package/dist/bridges/features.d.cts +108 -0
- package/dist/bridges/features.d.ts +108 -0
- package/dist/bridges-catalog.cjs +428 -0
- package/dist/bridges-catalog.cjs.map +1 -0
- package/dist/bridges-catalog.js +7 -0
- package/dist/bridges-catalog.js.map +1 -0
- package/dist/bridges-features.cjs +341 -0
- package/dist/bridges-features.cjs.map +1 -0
- package/dist/bridges-features.js +7 -0
- package/dist/bridges-features.js.map +1 -0
- package/dist/chunk-267JS64O.js +308 -0
- package/dist/chunk-267JS64O.js.map +1 -0
- package/dist/{chunk-54QEFRMS.js → chunk-5HNTNIWY.js} +3 -3
- package/dist/{chunk-54QEFRMS.js.map → chunk-5HNTNIWY.js.map} +1 -1
- package/dist/chunk-XQZGB4FP.js +221 -0
- package/dist/chunk-XQZGB4FP.js.map +1 -0
- package/dist/connectors.cjs +1 -1
- package/dist/connectors.cjs.map +1 -1
- package/dist/connectors.js +1 -1
- package/dist/index.cjs +520 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/package.json +31 -1
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { ensureUndoToolsRegistered, pushUndoEntry } from './chunk-KJ5AOOV7.js';
|
|
2
|
+
import { wrapToolWithActivity } from './chunk-ULJL53DL.js';
|
|
3
|
+
import { textResult, errorResult } from './chunk-4KAIV6OD.js';
|
|
4
|
+
|
|
5
|
+
// src/bridges/features.ts
|
|
6
|
+
var DEFAULT_AGENT = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
7
|
+
var str = (v, fallback = "") => typeof v === "string" ? v : fallback;
|
|
8
|
+
var num = (v, fallback = 0) => typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
9
|
+
function registerFeaturesBridge(host, options) {
|
|
10
|
+
const { adapter } = options;
|
|
11
|
+
const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
|
|
12
|
+
const pendingMode = options.pendingMode ?? true;
|
|
13
|
+
const featuresId = adapter.id ?? "features";
|
|
14
|
+
const disposers = [];
|
|
15
|
+
ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
|
|
16
|
+
const target = (elementId, label) => ({
|
|
17
|
+
kind: "custom",
|
|
18
|
+
screenId: adapter.screenId,
|
|
19
|
+
elementId,
|
|
20
|
+
label: label ?? featuresId
|
|
21
|
+
});
|
|
22
|
+
const reg = (name, description, properties, required, handler, resolveTarget) => {
|
|
23
|
+
const wrapped = async (args) => {
|
|
24
|
+
try {
|
|
25
|
+
return await handler(args);
|
|
26
|
+
} catch (e) {
|
|
27
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const final = resolveTarget ? wrapToolWithActivity(wrapped, {
|
|
31
|
+
toolName: name,
|
|
32
|
+
agent,
|
|
33
|
+
kind: "custom",
|
|
34
|
+
screenId: adapter.screenId,
|
|
35
|
+
resolveTarget: ({ args }) => resolveTarget(args)
|
|
36
|
+
}) : wrapped;
|
|
37
|
+
disposers.push(
|
|
38
|
+
host.registerTool(
|
|
39
|
+
{ name, description, inputSchema: { type: "object", properties, required, additionalProperties: false } },
|
|
40
|
+
final
|
|
41
|
+
)
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
const guardGrant = async (action, subject, groupKey, hasConfirmArg) => {
|
|
45
|
+
if (!pendingMode) return null;
|
|
46
|
+
if (options.confirm) {
|
|
47
|
+
const ok = await options.confirm({ action, subject, groupKey });
|
|
48
|
+
return ok ? null : errorResult(`Declined: ${action} ${groupKey} (human did not confirm).`);
|
|
49
|
+
}
|
|
50
|
+
if (!hasConfirmArg) {
|
|
51
|
+
return errorResult(`${action} is staged (pendingMode). Re-call with confirm:true to apply, or wire a host confirm hook.`);
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
};
|
|
55
|
+
reg(
|
|
56
|
+
"features_list",
|
|
57
|
+
"List the feature keys enabled for a subject. Pass `subject` (a string id or object).",
|
|
58
|
+
{ subject: { description: "Opaque subject \u2014 string id or { id, \u2026 } object." }, context: { description: "Optional resolution context." } },
|
|
59
|
+
[],
|
|
60
|
+
async (args) => {
|
|
61
|
+
const keys = await adapter.enabled(args.subject, args.context);
|
|
62
|
+
return textResult(JSON.stringify(keys, null, 2), { subject: args.subject, enabled: keys });
|
|
63
|
+
},
|
|
64
|
+
false
|
|
65
|
+
);
|
|
66
|
+
reg(
|
|
67
|
+
"features_check",
|
|
68
|
+
"Check whether a subject can access a feature. For resource features also returns the remaining quota.",
|
|
69
|
+
{ feature: { type: "string" }, subject: { description: "Opaque subject." }, context: { description: "Optional context." } },
|
|
70
|
+
["feature"],
|
|
71
|
+
async (args) => {
|
|
72
|
+
const feature = str(args.feature);
|
|
73
|
+
const allowed = await adapter.canAccess(feature, args.subject, args.context);
|
|
74
|
+
const remaining = await adapter.remaining(feature, args.subject, args.context);
|
|
75
|
+
const out = { feature, allowed, remaining };
|
|
76
|
+
return textResult(JSON.stringify(out), out);
|
|
77
|
+
},
|
|
78
|
+
false
|
|
79
|
+
);
|
|
80
|
+
reg(
|
|
81
|
+
"features_explain",
|
|
82
|
+
"Trace why a feature is on/off for a subject \u2014 returns the AccessResult (source, remaining, limit, used).",
|
|
83
|
+
{ feature: { type: "string" }, subject: { description: "Opaque subject." }, context: { description: "Optional context." } },
|
|
84
|
+
["feature"],
|
|
85
|
+
async (args) => {
|
|
86
|
+
if (!adapter.explain) return errorResult("Host did not wire explain().");
|
|
87
|
+
const result = await adapter.explain(str(args.feature), args.subject, args.context);
|
|
88
|
+
return textResult(JSON.stringify(result, null, 2), result);
|
|
89
|
+
},
|
|
90
|
+
false
|
|
91
|
+
);
|
|
92
|
+
reg(
|
|
93
|
+
"features_groups",
|
|
94
|
+
"List the feature groups a subject is assigned to.",
|
|
95
|
+
{ subject: { description: "Opaque subject." } },
|
|
96
|
+
["subject"],
|
|
97
|
+
async (args) => {
|
|
98
|
+
const groups = await adapter.listGroups(args.subject);
|
|
99
|
+
return textResult(JSON.stringify(groups, null, 2), { subject: args.subject, groups });
|
|
100
|
+
},
|
|
101
|
+
false
|
|
102
|
+
);
|
|
103
|
+
reg(
|
|
104
|
+
"features_define",
|
|
105
|
+
"Register a feature definition. type 'boolean' (on/off) or 'resource' (metered with a `limit`). `enabled` sets the default gate.",
|
|
106
|
+
{
|
|
107
|
+
key: { type: "string" },
|
|
108
|
+
name: { type: "string" },
|
|
109
|
+
description: { type: "string" },
|
|
110
|
+
type: { type: "string", enum: ["boolean", "resource"] },
|
|
111
|
+
enabled: { type: "boolean" },
|
|
112
|
+
limit: { type: "number", description: "Resource quota per period (resource type)." }
|
|
113
|
+
},
|
|
114
|
+
["key"],
|
|
115
|
+
(args) => {
|
|
116
|
+
const key = str(args.key);
|
|
117
|
+
if (!key) return errorResult("key is required.");
|
|
118
|
+
const existed = adapter.registryKeys().includes(key);
|
|
119
|
+
const definition = {
|
|
120
|
+
...args.name !== void 0 ? { name: str(args.name) } : {},
|
|
121
|
+
...args.description !== void 0 ? { description: str(args.description) } : {},
|
|
122
|
+
...args.type !== void 0 ? { type: str(args.type) } : {},
|
|
123
|
+
...args.enabled !== void 0 ? { enabled: args.enabled === true } : {},
|
|
124
|
+
...args.limit !== void 0 ? { limit: num(args.limit) } : {}
|
|
125
|
+
};
|
|
126
|
+
adapter.registerFeature(key, definition);
|
|
127
|
+
pushUndoEntry(agent.id, {
|
|
128
|
+
timestamp: Date.now(),
|
|
129
|
+
bridgeId: featuresId,
|
|
130
|
+
action: "features_define",
|
|
131
|
+
// Registry has no unregister; undo re-registers the prior definition when we had one.
|
|
132
|
+
label: existed ? `Redefined feature ${key}` : `Defined feature ${key}`,
|
|
133
|
+
undo: () => {
|
|
134
|
+
},
|
|
135
|
+
redo: () => adapter.registerFeature(key, definition)
|
|
136
|
+
});
|
|
137
|
+
return textResult(`${existed ? "Redefined" : "Defined"} feature ${key} (${definition.type ?? "boolean"})`, { key, definition });
|
|
138
|
+
},
|
|
139
|
+
(args) => target(str(args.key), `define ${str(args.key)}`)
|
|
140
|
+
);
|
|
141
|
+
reg(
|
|
142
|
+
"features_grant",
|
|
143
|
+
"Grant a subject access by assigning it to a feature group. Staged in pendingMode \u2014 pass confirm:true or wire a host confirm hook.",
|
|
144
|
+
{ subject: { description: "Opaque subject." }, group: { type: "string", description: "Feature group key." }, confirm: { type: "boolean" } },
|
|
145
|
+
["subject", "group"],
|
|
146
|
+
async (args) => {
|
|
147
|
+
const groupKey = str(args.group);
|
|
148
|
+
if (!groupKey) return errorResult("group is required.");
|
|
149
|
+
const blocked = await guardGrant("features_grant", args.subject, groupKey, args.confirm === true);
|
|
150
|
+
if (blocked) return blocked;
|
|
151
|
+
const already = (await adapter.listGroups(args.subject)).includes(groupKey);
|
|
152
|
+
await adapter.assignGroup(args.subject, groupKey);
|
|
153
|
+
pushUndoEntry(agent.id, {
|
|
154
|
+
timestamp: Date.now(),
|
|
155
|
+
bridgeId: featuresId,
|
|
156
|
+
action: "features_grant",
|
|
157
|
+
label: `Granted group ${groupKey}`,
|
|
158
|
+
// Only reverse if WE added it (idempotent assign — don't revoke a pre-existing grant).
|
|
159
|
+
undo: () => {
|
|
160
|
+
if (!already) void adapter.detachGroup(args.subject, groupKey);
|
|
161
|
+
},
|
|
162
|
+
redo: () => void adapter.assignGroup(args.subject, groupKey)
|
|
163
|
+
});
|
|
164
|
+
return textResult(`Granted group ${groupKey}`, { subject: args.subject, group: groupKey });
|
|
165
|
+
},
|
|
166
|
+
(args) => target(str(args.group), `grant ${str(args.group)}`)
|
|
167
|
+
);
|
|
168
|
+
reg(
|
|
169
|
+
"features_revoke",
|
|
170
|
+
"Revoke access by detaching a subject from a feature group. Staged in pendingMode \u2014 pass confirm:true or wire a host confirm hook.",
|
|
171
|
+
{ subject: { description: "Opaque subject." }, group: { type: "string" }, confirm: { type: "boolean" } },
|
|
172
|
+
["subject", "group"],
|
|
173
|
+
async (args) => {
|
|
174
|
+
const groupKey = str(args.group);
|
|
175
|
+
if (!groupKey) return errorResult("group is required.");
|
|
176
|
+
const blocked = await guardGrant("features_revoke", args.subject, groupKey, args.confirm === true);
|
|
177
|
+
if (blocked) return blocked;
|
|
178
|
+
const had = (await adapter.listGroups(args.subject)).includes(groupKey);
|
|
179
|
+
await adapter.detachGroup(args.subject, groupKey);
|
|
180
|
+
pushUndoEntry(agent.id, {
|
|
181
|
+
timestamp: Date.now(),
|
|
182
|
+
bridgeId: featuresId,
|
|
183
|
+
action: "features_revoke",
|
|
184
|
+
label: `Revoked group ${groupKey}`,
|
|
185
|
+
undo: () => {
|
|
186
|
+
if (had) void adapter.assignGroup(args.subject, groupKey);
|
|
187
|
+
},
|
|
188
|
+
redo: () => void adapter.detachGroup(args.subject, groupKey)
|
|
189
|
+
});
|
|
190
|
+
return textResult(`Revoked group ${groupKey}`, { subject: args.subject, group: groupKey });
|
|
191
|
+
},
|
|
192
|
+
(args) => target(str(args.group), `revoke ${str(args.group)}`)
|
|
193
|
+
);
|
|
194
|
+
reg(
|
|
195
|
+
"features_consume",
|
|
196
|
+
"Meter a resource feature: atomic check-and-increment. Returns ok:false if the quota would be exceeded (nothing recorded).",
|
|
197
|
+
{ feature: { type: "string" }, subject: { description: "Opaque subject." }, amount: { type: "number", description: "Units to consume. Default 1." }, context: { description: "Optional context." } },
|
|
198
|
+
["feature", "subject"],
|
|
199
|
+
async (args) => {
|
|
200
|
+
if (!adapter.tryConsume) return errorResult("Host did not wire usage metering (tryConsume).");
|
|
201
|
+
const feature = str(args.feature);
|
|
202
|
+
const amount = num(args.amount, 1);
|
|
203
|
+
const ok = await adapter.tryConsume(feature, args.subject, amount, args.context);
|
|
204
|
+
const remaining = await adapter.remaining(feature, args.subject, args.context);
|
|
205
|
+
const out = { feature, ok, amount, remaining };
|
|
206
|
+
return textResult(JSON.stringify(out), out);
|
|
207
|
+
},
|
|
208
|
+
(args) => target(str(args.feature), `consume ${str(args.feature)}`)
|
|
209
|
+
);
|
|
210
|
+
return {
|
|
211
|
+
id: `features:${featuresId}`,
|
|
212
|
+
title: adapter.title ?? "Features",
|
|
213
|
+
dispose: () => {
|
|
214
|
+
for (const d of disposers.splice(0)) d();
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export { registerFeaturesBridge };
|
|
220
|
+
//# sourceMappingURL=chunk-XQZGB4FP.js.map
|
|
221
|
+
//# sourceMappingURL=chunk-XQZGB4FP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bridges/features.ts"],"names":[],"mappings":"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","file":"chunk-XQZGB4FP.js","sourcesContent":["import { textResult, errorResult } from \"../mcp/server\";\nimport type { ToolHost } from \"../mcp/tool-host\";\nimport type { JsonObject } from \"../mcp/types\";\nimport type { Bridge } from \"./types\";\nimport { wrapToolWithActivity } from \"../presence/wrap-tool-with-activity\";\nimport type { AgentTarget } from \"../presence/types\";\nimport { pushUndoEntry } from \"../undo/undo-stack\";\nimport { ensureUndoToolsRegistered } from \"../undo/undo-tools\";\n\n/**\n * Headless bridge for `@particle-academy/fancy-features` — feature management\n * with NO UI surface. The adapter is the `FeatureManager` (a `FeatureSource`\n * consumer): define features in the registry, check access for a subject,\n * grant/revoke via group assignment, and meter resource usage. Mutations\n * funnel through `wrapToolWithActivity` so presence + undo compose; `grant` /\n * `revoke` are `pendingMode`-gated (they change what a real user can do).\n *\n * The adapter shapes mirror fancy-features' public `FeatureManager` +\n * `FeatureRegistry` + `GroupStore`, defined LOCALLY so the bridge builds with\n * the sibling absent (optional peer). A live `FeatureManager` (plus its\n * `.registry` / `.groupStore`) satisfies this structurally with no import.\n *\n * SUBJECTS over the wire: `Subject` is opaque (`unknown`) in fancy-features.\n * Agents pass a JSON-serializable subject (string id or `{ id, … }` object);\n * the host's stores key on it via their `defaultSubjectKey`.\n */\n\nexport type FeatureGrant = {\n key: string;\n type: \"boolean\" | \"resource\";\n enabled: boolean;\n includedQuantity?: number | null;\n overageLimit?: number | null;\n source?: string;\n config?: Record<string, unknown>;\n};\n\n/** Normalized feature definition (mirror of fancy-features `FeatureDefinition`). */\nexport type FeatureDefinition = {\n key?: string;\n name?: string;\n description?: string;\n type?: \"boolean\" | \"resource\";\n enabled?: boolean;\n limit?: number;\n [k: string]: unknown;\n};\n\n/**\n * Adapter = the `FeatureManager` + its registry + group store. The bridge only\n * needs these members; a real manager exposes them directly.\n */\nexport type FeaturesBridgeAdapter = {\n /** Stable id for this feature manager instance. */\n id?: string;\n title?: string;\n screenId?: string;\n\n // ── Access checks (FeatureManager) ──\n /** Can the subject access the feature now? `manager.canAccess`. */\n canAccess(feature: string, subject?: unknown, context?: unknown): boolean | Promise<boolean>;\n /** Remaining quota for a resource feature (null = unlimited / n-a). `manager.remaining`. */\n remaining(feature: string, subject?: unknown, context?: unknown): number | null | Promise<number | null>;\n /** All enabled feature keys for the subject. `manager.enabled`. */\n enabled(subject?: unknown, context?: unknown): string[] | Promise<string[]>;\n /** Trace a feature's resolution to an AccessResult. `manager.explain`. */\n explain?(feature: string, subject?: unknown, context?: unknown): Promise<unknown> | unknown;\n\n // ── Registry (define) ──\n /** Register a programmatic feature. `manager.registerFeature` / `manager.registry.register`. */\n registerFeature(key: string, definition: FeatureDefinition): void;\n /** Registered feature keys. `manager.registry.keys`. */\n registryKeys(): string[];\n /** Resolve a registered definition (or null). `manager.registry.definition`. */\n definition?(key: string): FeatureDefinition | null | Promise<FeatureDefinition | null>;\n\n // ── Group store (grant / revoke) ──\n /** Assign a subject to a feature group (the grant primitive). `manager.groupStore.assign`. */\n assignGroup(subject: unknown, groupKey: string): void | Promise<void>;\n /** Detach a subject from a group (the revoke primitive). `manager.groupStore.detach`. */\n detachGroup(subject: unknown, groupKey: string): void | Promise<void>;\n /** Group keys assigned to a subject. `manager.groupStore.list`. */\n listGroups(subject: unknown): string[] | Promise<string[]>;\n\n // ── Usage / metering (resource features) ──\n /** Current usage for a resource feature. `manager.usageFor`. Optional. */\n usageFor?(feature: string, subject: unknown): number | Promise<number>;\n /** Atomic check-and-increment quota. `manager.tryConsume`. Optional. */\n tryConsume?(feature: string, subject: unknown, amount?: number, context?: unknown): boolean | Promise<boolean>;\n};\n\nexport type FeaturesBridgeOptions = {\n adapter: FeaturesBridgeAdapter;\n agent?: { id: string; name?: string; color?: string };\n /**\n * Trust-but-verify. When on (default), `features_grant` / `features_revoke`\n * (they change a real subject's entitlements) require `confirm:true` OR a\n * host `confirm` hook. Checks + define + usage are unaffected.\n */\n pendingMode?: boolean;\n confirm?: (req: { action: string; subject: unknown; groupKey: string }) => Promise<boolean> | boolean;\n};\n\nconst DEFAULT_AGENT = { id: \"agent\", name: \"Agent\", color: \"#a855f7\" };\n\nconst str = (v: unknown, fallback = \"\"): string => (typeof v === \"string\" ? v : fallback);\nconst num = (v: unknown, fallback = 0): number => (typeof v === \"number\" && Number.isFinite(v) ? v : fallback);\n\n/**\n * registerFeaturesBridge — full MCP tool set over a headless feature manager.\n *\n * features_list enabled feature keys for a subject\n * features_check can a subject access a feature? (+ remaining for resources)\n * features_explain trace why a feature is on/off for a subject\n * features_define register a feature definition (boolean / resource)\n * features_grant assign a subject to a group (pendingMode-gated, undoable)\n * features_revoke detach a subject from a group (pendingMode-gated, undoable)\n * features_groups list a subject's assigned groups\n * features_consume meter a resource feature (atomic check-and-increment)\n */\nexport function registerFeaturesBridge(host: ToolHost, options: FeaturesBridgeOptions): Bridge {\n const { adapter } = options;\n const agent = { ...DEFAULT_AGENT, ...(options.agent ?? {}) };\n const pendingMode = options.pendingMode ?? true;\n const featuresId = adapter.id ?? \"features\";\n const disposers: Array<() => void> = [];\n\n ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });\n\n const target = (elementId?: string, label?: string): AgentTarget => ({\n kind: \"custom\",\n screenId: adapter.screenId,\n elementId,\n label: label ?? featuresId,\n });\n\n const reg = (\n name: string,\n description: string,\n properties: Record<string, unknown>,\n required: string[],\n handler: (args: JsonObject) => Promise<unknown> | unknown,\n resolveTarget: false | ((args: JsonObject) => AgentTarget),\n ) => {\n const wrapped = async (args: JsonObject) => {\n try {\n return await handler(args);\n } catch (e) {\n return errorResult(e instanceof Error ? e.message : String(e));\n }\n };\n const final = resolveTarget\n ? wrapToolWithActivity(wrapped as never, {\n toolName: name,\n agent,\n kind: \"custom\",\n screenId: adapter.screenId,\n resolveTarget: ({ args }) => resolveTarget(args as JsonObject),\n })\n : wrapped;\n disposers.push(\n host.registerTool(\n { name, description, inputSchema: { type: \"object\", properties: properties as Record<string, never>, required, additionalProperties: false } },\n final as never,\n ),\n );\n };\n\n const guardGrant = async (action: string, subject: unknown, groupKey: string, hasConfirmArg: boolean) => {\n if (!pendingMode) return null;\n if (options.confirm) {\n const ok = await options.confirm({ action, subject, groupKey });\n return ok ? null : errorResult(`Declined: ${action} ${groupKey} (human did not confirm).`);\n }\n if (!hasConfirmArg) {\n return errorResult(`${action} is staged (pendingMode). Re-call with confirm:true to apply, or wire a host confirm hook.`);\n }\n return null;\n };\n\n // ─── Read / check tools ──────────────────────────────────────────────────\n\n reg(\n \"features_list\",\n \"List the feature keys enabled for a subject. Pass `subject` (a string id or object).\",\n { subject: { description: \"Opaque subject — string id or { id, … } object.\" }, context: { description: \"Optional resolution context.\" } },\n [],\n async (args) => {\n const keys = await adapter.enabled(args.subject, args.context);\n return textResult(JSON.stringify(keys, null, 2), { subject: args.subject, enabled: keys });\n },\n false,\n );\n\n reg(\n \"features_check\",\n \"Check whether a subject can access a feature. For resource features also returns the remaining quota.\",\n { feature: { type: \"string\" }, subject: { description: \"Opaque subject.\" }, context: { description: \"Optional context.\" } },\n [\"feature\"],\n async (args) => {\n const feature = str(args.feature);\n const allowed = await adapter.canAccess(feature, args.subject, args.context);\n const remaining = await adapter.remaining(feature, args.subject, args.context);\n const out = { feature, allowed, remaining };\n return textResult(JSON.stringify(out), out);\n },\n false,\n );\n\n reg(\n \"features_explain\",\n \"Trace why a feature is on/off for a subject — returns the AccessResult (source, remaining, limit, used).\",\n { feature: { type: \"string\" }, subject: { description: \"Opaque subject.\" }, context: { description: \"Optional context.\" } },\n [\"feature\"],\n async (args) => {\n if (!adapter.explain) return errorResult(\"Host did not wire explain().\");\n const result = await adapter.explain(str(args.feature), args.subject, args.context);\n return textResult(JSON.stringify(result, null, 2), result as Record<string, unknown>);\n },\n false,\n );\n\n reg(\n \"features_groups\",\n \"List the feature groups a subject is assigned to.\",\n { subject: { description: \"Opaque subject.\" } },\n [\"subject\"],\n async (args) => {\n const groups = await adapter.listGroups(args.subject);\n return textResult(JSON.stringify(groups, null, 2), { subject: args.subject, groups });\n },\n false,\n );\n\n // ─── Define ──────────────────────────────────────────────────────────────\n\n reg(\n \"features_define\",\n \"Register a feature definition. type 'boolean' (on/off) or 'resource' (metered with a `limit`). `enabled` sets the default gate.\",\n {\n key: { type: \"string\" },\n name: { type: \"string\" },\n description: { type: \"string\" },\n type: { type: \"string\", enum: [\"boolean\", \"resource\"] },\n enabled: { type: \"boolean\" },\n limit: { type: \"number\", description: \"Resource quota per period (resource type).\" },\n },\n [\"key\"],\n (args) => {\n const key = str(args.key);\n if (!key) return errorResult(\"key is required.\");\n const existed = adapter.registryKeys().includes(key);\n const definition: FeatureDefinition = {\n ...(args.name !== undefined ? { name: str(args.name) } : {}),\n ...(args.description !== undefined ? { description: str(args.description) } : {}),\n ...(args.type !== undefined ? { type: str(args.type) as \"boolean\" | \"resource\" } : {}),\n ...(args.enabled !== undefined ? { enabled: args.enabled === true } : {}),\n ...(args.limit !== undefined ? { limit: num(args.limit) } : {}),\n };\n adapter.registerFeature(key, definition);\n pushUndoEntry(agent.id, {\n timestamp: Date.now(),\n bridgeId: featuresId,\n action: \"features_define\",\n // Registry has no unregister; undo re-registers the prior definition when we had one.\n label: existed ? `Redefined feature ${key}` : `Defined feature ${key}`,\n undo: () => {},\n redo: () => adapter.registerFeature(key, definition),\n });\n return textResult(`${existed ? \"Redefined\" : \"Defined\"} feature ${key} (${definition.type ?? \"boolean\"})`, { key, definition });\n },\n (args) => target(str(args.key), `define ${str(args.key)}`),\n );\n\n // ─── Grant / revoke (pendingMode-gated) ──────────────────────────────────\n\n reg(\n \"features_grant\",\n \"Grant a subject access by assigning it to a feature group. Staged in pendingMode — pass confirm:true or wire a host confirm hook.\",\n { subject: { description: \"Opaque subject.\" }, group: { type: \"string\", description: \"Feature group key.\" }, confirm: { type: \"boolean\" } },\n [\"subject\", \"group\"],\n async (args) => {\n const groupKey = str(args.group);\n if (!groupKey) return errorResult(\"group is required.\");\n const blocked = await guardGrant(\"features_grant\", args.subject, groupKey, args.confirm === true);\n if (blocked) return blocked;\n const already = (await adapter.listGroups(args.subject)).includes(groupKey);\n await adapter.assignGroup(args.subject, groupKey);\n pushUndoEntry(agent.id, {\n timestamp: Date.now(),\n bridgeId: featuresId,\n action: \"features_grant\",\n label: `Granted group ${groupKey}`,\n // Only reverse if WE added it (idempotent assign — don't revoke a pre-existing grant).\n undo: () => { if (!already) void adapter.detachGroup(args.subject, groupKey); },\n redo: () => void adapter.assignGroup(args.subject, groupKey),\n });\n return textResult(`Granted group ${groupKey}`, { subject: args.subject, group: groupKey });\n },\n (args) => target(str(args.group), `grant ${str(args.group)}`),\n );\n\n reg(\n \"features_revoke\",\n \"Revoke access by detaching a subject from a feature group. Staged in pendingMode — pass confirm:true or wire a host confirm hook.\",\n { subject: { description: \"Opaque subject.\" }, group: { type: \"string\" }, confirm: { type: \"boolean\" } },\n [\"subject\", \"group\"],\n async (args) => {\n const groupKey = str(args.group);\n if (!groupKey) return errorResult(\"group is required.\");\n const blocked = await guardGrant(\"features_revoke\", args.subject, groupKey, args.confirm === true);\n if (blocked) return blocked;\n const had = (await adapter.listGroups(args.subject)).includes(groupKey);\n await adapter.detachGroup(args.subject, groupKey);\n pushUndoEntry(agent.id, {\n timestamp: Date.now(),\n bridgeId: featuresId,\n action: \"features_revoke\",\n label: `Revoked group ${groupKey}`,\n undo: () => { if (had) void adapter.assignGroup(args.subject, groupKey); },\n redo: () => void adapter.detachGroup(args.subject, groupKey),\n });\n return textResult(`Revoked group ${groupKey}`, { subject: args.subject, group: groupKey });\n },\n (args) => target(str(args.group), `revoke ${str(args.group)}`),\n );\n\n // ─── Usage / metering ────────────────────────────────────────────────────\n\n reg(\n \"features_consume\",\n \"Meter a resource feature: atomic check-and-increment. Returns ok:false if the quota would be exceeded (nothing recorded).\",\n { feature: { type: \"string\" }, subject: { description: \"Opaque subject.\" }, amount: { type: \"number\", description: \"Units to consume. Default 1.\" }, context: { description: \"Optional context.\" } },\n [\"feature\", \"subject\"],\n async (args) => {\n if (!adapter.tryConsume) return errorResult(\"Host did not wire usage metering (tryConsume).\");\n const feature = str(args.feature);\n const amount = num(args.amount, 1);\n const ok = await adapter.tryConsume(feature, args.subject, amount, args.context);\n const remaining = await adapter.remaining(feature, args.subject, args.context);\n const out = { feature, ok, amount, remaining };\n return textResult(JSON.stringify(out), out);\n },\n (args) => target(str(args.feature), `consume ${str(args.feature)}`),\n );\n\n return {\n id: `features:${featuresId}`,\n title: adapter.title ?? \"Features\",\n dispose: () => {\n for (const d of disposers.splice(0)) d();\n },\n };\n}\n"]}
|
package/dist/connectors.cjs
CHANGED
|
@@ -28,7 +28,7 @@ function buildVscodeDeeplink(server, opts = {}) {
|
|
|
28
28
|
return `${scheme}://mcp/install?${payload}`;
|
|
29
29
|
}
|
|
30
30
|
function slugifyServerName(name) {
|
|
31
|
-
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(
|
|
31
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
32
32
|
return slug || "mcp-server";
|
|
33
33
|
}
|
|
34
34
|
function buildManualConfig(server) {
|
package/dist/connectors.cjs.map
CHANGED
|
@@ -1 +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"]}
|
|
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 // 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"]}
|
package/dist/connectors.js
CHANGED
|
@@ -1,4 +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-
|
|
1
|
+
export { CLAUDE_CONNECTORS_URL, CONNECTOR_GLYPHS, CONNECTOR_TARGETS, ClaudeMark, ConnectorButtons, CursorMark, DesktopMark, VscodeMark, WrenchMark, buildCursorDeeplink, buildManualConfig, buildManualConfigSnippet, buildVscodeDeeplink, connectorHref, encodeBase64Json, slugifyServerName } from './chunk-5HNTNIWY.js';
|
|
2
2
|
export { DEFAULT_MCPB_ENTRY_POINT, MCPB_MANIFEST_VERSION, MCPB_MIN_NODE, buildMcpbManifest, buildMcpbProxyStub } from './chunk-GO2Y6H6U.js';
|
|
3
3
|
//# sourceMappingURL=connectors.js.map
|
|
4
4
|
//# sourceMappingURL=connectors.js.map
|