@trops/dash-core 0.1.446 → 0.1.448
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/electron/index.js +422 -30
- package/dist/electron/index.js.map +1 -1
- package/dist/index.esm.js +137 -84
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +137 -84
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/electron/index.js
CHANGED
|
@@ -424,6 +424,11 @@ const MCP_GET_CATALOG$1 = "mcp-get-catalog";
|
|
|
424
424
|
const MCP_GET_CATALOG_COMPLETE = "mcp-get-catalog-complete";
|
|
425
425
|
const MCP_GET_CATALOG_ERROR = "mcp-get-catalog-error";
|
|
426
426
|
|
|
427
|
+
const MCP_GET_KNOWN_EXTERNAL$1 = "mcp-get-known-external";
|
|
428
|
+
|
|
429
|
+
const MCP_INSTALL_KNOWN_EXTERNAL_CONFIRM$2 = "mcp-install-known-external-confirm";
|
|
430
|
+
const MCP_INSTALL_KNOWN_EXTERNAL_RESULT$2 = "mcp-install-known-external-result";
|
|
431
|
+
|
|
427
432
|
const MCP_RUN_AUTH$1 = "mcp-run-auth";
|
|
428
433
|
const MCP_RUN_AUTH_COMPLETE = "mcp-run-auth-complete";
|
|
429
434
|
const MCP_RUN_AUTH_ERROR = "mcp-run-auth-error";
|
|
@@ -453,6 +458,9 @@ var mcpEvents$1 = {
|
|
|
453
458
|
MCP_GET_CATALOG: MCP_GET_CATALOG$1,
|
|
454
459
|
MCP_GET_CATALOG_COMPLETE,
|
|
455
460
|
MCP_GET_CATALOG_ERROR,
|
|
461
|
+
MCP_GET_KNOWN_EXTERNAL: MCP_GET_KNOWN_EXTERNAL$1,
|
|
462
|
+
MCP_INSTALL_KNOWN_EXTERNAL_CONFIRM: MCP_INSTALL_KNOWN_EXTERNAL_CONFIRM$2,
|
|
463
|
+
MCP_INSTALL_KNOWN_EXTERNAL_RESULT: MCP_INSTALL_KNOWN_EXTERNAL_RESULT$2,
|
|
456
464
|
MCP_RUN_AUTH: MCP_RUN_AUTH$1,
|
|
457
465
|
MCP_RUN_AUTH_COMPLETE,
|
|
458
466
|
MCP_RUN_AUTH_ERROR,
|
|
@@ -5296,7 +5304,7 @@ const layoutController$1 = {
|
|
|
5296
5304
|
|
|
5297
5305
|
var layoutController_1 = layoutController$1;
|
|
5298
5306
|
|
|
5299
|
-
var mcpController$
|
|
5307
|
+
var mcpController$4 = {exports: {}};
|
|
5300
5308
|
|
|
5301
5309
|
var streamableHttp$1 = {};
|
|
5302
5310
|
|
|
@@ -26032,7 +26040,7 @@ async function refreshGoogleOAuthToken(tokenRefresh) {
|
|
|
26032
26040
|
console.log("[mcpController] Google OAuth token refreshed successfully");
|
|
26033
26041
|
}
|
|
26034
26042
|
|
|
26035
|
-
const mcpController$
|
|
26043
|
+
const mcpController$3 = {
|
|
26036
26044
|
/**
|
|
26037
26045
|
* startServer
|
|
26038
26046
|
* Start an MCP server with the given config and credentials
|
|
@@ -26070,13 +26078,13 @@ const mcpController$2 = {
|
|
|
26070
26078
|
try {
|
|
26071
26079
|
// Stop if in stale/error state
|
|
26072
26080
|
if (activeServers.has(serverName)) {
|
|
26073
|
-
await mcpController$
|
|
26081
|
+
await mcpController$3.stopServer(win, serverName);
|
|
26074
26082
|
}
|
|
26075
26083
|
|
|
26076
26084
|
// Merge with catalog entry to pick up updated command/args
|
|
26077
26085
|
// (saved provider config may reference a stale or archived package)
|
|
26078
26086
|
try {
|
|
26079
|
-
const { catalog } = mcpController$
|
|
26087
|
+
const { catalog } = mcpController$3.getCatalog(win);
|
|
26080
26088
|
const catalogEntry = (catalog || []).find(
|
|
26081
26089
|
(entry) => entry.name === serverName,
|
|
26082
26090
|
);
|
|
@@ -26602,6 +26610,48 @@ const mcpController$2 = {
|
|
|
26602
26610
|
}
|
|
26603
26611
|
},
|
|
26604
26612
|
|
|
26613
|
+
/**
|
|
26614
|
+
* getKnownExternalCatalog
|
|
26615
|
+
* Load the curated allow-list of MCP servers known to exist outside the
|
|
26616
|
+
* built-in catalog. Used by the AI Widget Builder to suggest installable
|
|
26617
|
+
* servers and as the trust boundary for `install_known_mcp_server` —
|
|
26618
|
+
* the install tool will reject any id that doesn't appear here.
|
|
26619
|
+
*
|
|
26620
|
+
* @returns {{ success, servers } | { error, message, servers }}
|
|
26621
|
+
*/
|
|
26622
|
+
getKnownExternalCatalog: () => {
|
|
26623
|
+
try {
|
|
26624
|
+
const catalogPath = path$c.join(
|
|
26625
|
+
__dirname,
|
|
26626
|
+
"..",
|
|
26627
|
+
"mcp",
|
|
26628
|
+
"knownExternalMcpServers.json",
|
|
26629
|
+
);
|
|
26630
|
+
|
|
26631
|
+
if (!fs$8.existsSync(catalogPath)) {
|
|
26632
|
+
return { success: true, servers: [] };
|
|
26633
|
+
}
|
|
26634
|
+
|
|
26635
|
+
const catalogData = fs$8.readFileSync(catalogPath, "utf8");
|
|
26636
|
+
const catalog = JSON.parse(catalogData);
|
|
26637
|
+
|
|
26638
|
+
return {
|
|
26639
|
+
success: true,
|
|
26640
|
+
servers: catalog.servers || [],
|
|
26641
|
+
};
|
|
26642
|
+
} catch (error) {
|
|
26643
|
+
console.error(
|
|
26644
|
+
"[mcpController] Error loading known-external catalog:",
|
|
26645
|
+
error,
|
|
26646
|
+
);
|
|
26647
|
+
return {
|
|
26648
|
+
error: true,
|
|
26649
|
+
message: error.message,
|
|
26650
|
+
servers: [],
|
|
26651
|
+
};
|
|
26652
|
+
}
|
|
26653
|
+
},
|
|
26654
|
+
|
|
26605
26655
|
/**
|
|
26606
26656
|
* listConnectedServers
|
|
26607
26657
|
* Returns all connected servers with their cached tool lists.
|
|
@@ -26765,17 +26815,17 @@ const mcpController$2 = {
|
|
|
26765
26815
|
);
|
|
26766
26816
|
const promises = [];
|
|
26767
26817
|
for (const [serverName] of activeServers) {
|
|
26768
|
-
promises.push(mcpController$
|
|
26818
|
+
promises.push(mcpController$3.stopServer(null, serverName));
|
|
26769
26819
|
}
|
|
26770
26820
|
await Promise.allSettled(promises);
|
|
26771
26821
|
console.log("[mcpController] All servers stopped");
|
|
26772
26822
|
},
|
|
26773
26823
|
};
|
|
26774
26824
|
|
|
26775
|
-
mcpController$
|
|
26776
|
-
mcpController$
|
|
26825
|
+
mcpController$4.exports = mcpController$3;
|
|
26826
|
+
mcpController$4.exports.refreshGoogleOAuthToken = refreshGoogleOAuthToken;
|
|
26777
26827
|
|
|
26778
|
-
var mcpControllerExports = mcpController$
|
|
26828
|
+
var mcpControllerExports = mcpController$4.exports;
|
|
26779
26829
|
|
|
26780
26830
|
/**
|
|
26781
26831
|
* Scoped package identity utilities.
|
|
@@ -51231,7 +51281,7 @@ var jsonSchemaToZod_1 = { jsonSchemaToZod: jsonSchemaToZod$1, jsonSchemaProperty
|
|
|
51231
51281
|
|
|
51232
51282
|
const https$2 = require$$8$1;
|
|
51233
51283
|
const { randomUUID } = require$$1$6;
|
|
51234
|
-
const { BrowserWindow } = require$$0$1;
|
|
51284
|
+
const { BrowserWindow: BrowserWindow$1 } = require$$0$1;
|
|
51235
51285
|
const { McpServer } = mcp;
|
|
51236
51286
|
const {
|
|
51237
51287
|
StreamableHTTPServerTransport,
|
|
@@ -51275,7 +51325,7 @@ function broadcastStateChanged(toolName, result) {
|
|
|
51275
51325
|
/* leave null */
|
|
51276
51326
|
}
|
|
51277
51327
|
const payload = { toolName, result: parsed };
|
|
51278
|
-
for (const win of BrowserWindow.getAllWindows()) {
|
|
51328
|
+
for (const win of BrowserWindow$1.getAllWindows()) {
|
|
51279
51329
|
if (!win.isDestroyed()) {
|
|
51280
51330
|
try {
|
|
51281
51331
|
win.webContents.send("dash-mcp:state-changed", payload);
|
|
@@ -51341,7 +51391,7 @@ const registeredPrompts = [];
|
|
|
51341
51391
|
* Register a tool to be exposed via the MCP server.
|
|
51342
51392
|
* Call this before starting the server (or restart after registering).
|
|
51343
51393
|
*/
|
|
51344
|
-
function registerTool$
|
|
51394
|
+
function registerTool$7(toolDef) {
|
|
51345
51395
|
registeredTools.push(toolDef);
|
|
51346
51396
|
}
|
|
51347
51397
|
|
|
@@ -51722,7 +51772,7 @@ const mcpDashServerController$4 = {
|
|
|
51722
51772
|
},
|
|
51723
51773
|
|
|
51724
51774
|
// Expose registration functions for other controllers
|
|
51725
|
-
registerTool: registerTool$
|
|
51775
|
+
registerTool: registerTool$7,
|
|
51726
51776
|
registerResource: registerResource$1,
|
|
51727
51777
|
registerPrompt: registerPrompt$1,
|
|
51728
51778
|
getServerContext,
|
|
@@ -61842,7 +61892,7 @@ async function handleListProviders$1() {
|
|
|
61842
61892
|
* add_provider — Adds a new provider with encrypted credentials.
|
|
61843
61893
|
* Credentials are accepted on input but never returned in the response.
|
|
61844
61894
|
*/
|
|
61845
|
-
async function handleAddProvider$
|
|
61895
|
+
async function handleAddProvider$2({
|
|
61846
61896
|
name,
|
|
61847
61897
|
type,
|
|
61848
61898
|
providerClass,
|
|
@@ -62667,7 +62717,7 @@ var toolHandlers$1 = {
|
|
|
62667
62717
|
handleSearchRegistryThemes: handleSearchRegistryThemes$1,
|
|
62668
62718
|
handleSearchRegistryDashboards: handleSearchRegistryDashboards$1,
|
|
62669
62719
|
handleListProviders: handleListProviders$1,
|
|
62670
|
-
handleAddProvider: handleAddProvider$
|
|
62720
|
+
handleAddProvider: handleAddProvider$2,
|
|
62671
62721
|
handleRemoveProvider: handleRemoveProvider$1,
|
|
62672
62722
|
handleGetSetupGuide: handleGetSetupGuide$1,
|
|
62673
62723
|
handleSetLayout: handleSetLayout$1,
|
|
@@ -62692,7 +62742,7 @@ var toolHandlers$1 = {
|
|
|
62692
62742
|
*/
|
|
62693
62743
|
|
|
62694
62744
|
const Anthropic = require$$0$9;
|
|
62695
|
-
const mcpController$
|
|
62745
|
+
const mcpController$2 = mcpControllerExports;
|
|
62696
62746
|
const cliController$1 = cliController_1;
|
|
62697
62747
|
const toolDefinitions = toolDefinitions$1;
|
|
62698
62748
|
const toolHandlers = toolHandlers$1;
|
|
@@ -62988,7 +63038,7 @@ const llmController$1 = {
|
|
|
62988
63038
|
isError = true;
|
|
62989
63039
|
} else {
|
|
62990
63040
|
try {
|
|
62991
|
-
const mcpResult = await mcpController$
|
|
63041
|
+
const mcpResult = await mcpController$2.callTool(
|
|
62992
63042
|
win,
|
|
62993
63043
|
serverName,
|
|
62994
63044
|
toolBlock.name,
|
|
@@ -74145,6 +74195,99 @@ function findWidget(registry, packageId) {
|
|
|
74145
74195
|
return null;
|
|
74146
74196
|
}
|
|
74147
74197
|
|
|
74198
|
+
/**
|
|
74199
|
+
* Dedup duplicate `{type: "..."}` entries inside a `providers: [...]`
|
|
74200
|
+
* array literal in a .dash.js source string. Mirrors the regex used at
|
|
74201
|
+
* AI-build write time in dash-electron's WidgetBuilderModal so old
|
|
74202
|
+
* AI-generated widgets get healed before publish (the runtime parse
|
|
74203
|
+
* dedup keeps consumers correct, but the raw .dash.js text on disk
|
|
74204
|
+
* stays dirty unless we rewrite it).
|
|
74205
|
+
*
|
|
74206
|
+
* Conservative: only handles a single-level array of object literals.
|
|
74207
|
+
* More exotic forms fall through unchanged and the runtime dedup picks
|
|
74208
|
+
* up the slack.
|
|
74209
|
+
*
|
|
74210
|
+
* @param {string} source
|
|
74211
|
+
* @returns {{ source: string, dropped: number }}
|
|
74212
|
+
*/
|
|
74213
|
+
function dedupProvidersInDashSource(source) {
|
|
74214
|
+
if (!source) return { source, dropped: 0 };
|
|
74215
|
+
let totalDropped = 0;
|
|
74216
|
+
const cleaned = source.replace(
|
|
74217
|
+
/(providers\s*:\s*\[)([^[\]]*?)(\])/,
|
|
74218
|
+
(match, head, body, tail) => {
|
|
74219
|
+
const chunks = body
|
|
74220
|
+
.split(/(\{[^{}]*\})/)
|
|
74221
|
+
.filter((s) => s && /\S/.test(s));
|
|
74222
|
+
const seenTypes = new Set();
|
|
74223
|
+
const kept = [];
|
|
74224
|
+
let dropped = 0;
|
|
74225
|
+
for (const chunk of chunks) {
|
|
74226
|
+
if (!chunk.startsWith("{")) continue;
|
|
74227
|
+
const typeMatch = chunk.match(/type\s*:\s*["']([^"']+)["']/);
|
|
74228
|
+
if (!typeMatch) {
|
|
74229
|
+
kept.push(chunk.trim());
|
|
74230
|
+
continue;
|
|
74231
|
+
}
|
|
74232
|
+
const t = typeMatch[1];
|
|
74233
|
+
if (seenTypes.has(t)) {
|
|
74234
|
+
dropped++;
|
|
74235
|
+
continue;
|
|
74236
|
+
}
|
|
74237
|
+
seenTypes.add(t);
|
|
74238
|
+
kept.push(chunk.trim());
|
|
74239
|
+
}
|
|
74240
|
+
if (dropped === 0) return match;
|
|
74241
|
+
totalDropped += dropped;
|
|
74242
|
+
return `${head}${kept.join(", ")}${tail}`;
|
|
74243
|
+
},
|
|
74244
|
+
);
|
|
74245
|
+
return { source: cleaned, dropped: totalDropped };
|
|
74246
|
+
}
|
|
74247
|
+
|
|
74248
|
+
/**
|
|
74249
|
+
* Walk a widget package's `.dash.js` files and rewrite any with
|
|
74250
|
+
* duplicate provider-type entries. Returns counts so the publish
|
|
74251
|
+
* caller can log what was healed. Errors are non-fatal — a single
|
|
74252
|
+
* unparseable .dash.js shouldn't block the whole publish.
|
|
74253
|
+
*/
|
|
74254
|
+
function cleanupProvidersInWidgetPackage(widgetPath) {
|
|
74255
|
+
const summary = { filesScanned: 0, filesRewritten: 0, totalDropped: 0 };
|
|
74256
|
+
try {
|
|
74257
|
+
const widgetsDir =
|
|
74258
|
+
findWidgetsDir(widgetPath) || path.join(widgetPath, "widgets");
|
|
74259
|
+
if (!fs.existsSync(widgetsDir)) return summary;
|
|
74260
|
+
for (const file of fs.readdirSync(widgetsDir)) {
|
|
74261
|
+
if (!file.endsWith(".dash.js")) continue;
|
|
74262
|
+
const filePath = path.join(widgetsDir, file);
|
|
74263
|
+
try {
|
|
74264
|
+
const original = fs.readFileSync(filePath, "utf8");
|
|
74265
|
+
summary.filesScanned++;
|
|
74266
|
+
const { source: deduped, dropped } =
|
|
74267
|
+
dedupProvidersInDashSource(original);
|
|
74268
|
+
if (dropped > 0 && deduped !== original) {
|
|
74269
|
+
fs.writeFileSync(filePath, deduped, "utf8");
|
|
74270
|
+
summary.filesRewritten++;
|
|
74271
|
+
summary.totalDropped += dropped;
|
|
74272
|
+
console.log(
|
|
74273
|
+
`[widgetRegistry] Cleaned ${dropped} duplicate provider(s) from ${file}`,
|
|
74274
|
+
);
|
|
74275
|
+
}
|
|
74276
|
+
} catch (err) {
|
|
74277
|
+
console.warn(
|
|
74278
|
+
`[widgetRegistry] cleanupProviders skip ${file}: ${err.message}`,
|
|
74279
|
+
);
|
|
74280
|
+
}
|
|
74281
|
+
}
|
|
74282
|
+
} catch (err) {
|
|
74283
|
+
console.warn(
|
|
74284
|
+
"[widgetRegistry] cleanupProvidersInWidgetPackage failed:",
|
|
74285
|
+
err.message,
|
|
74286
|
+
);
|
|
74287
|
+
}
|
|
74288
|
+
return summary;
|
|
74289
|
+
}
|
|
74290
|
+
|
|
74148
74291
|
/**
|
|
74149
74292
|
* Scan a widget package directory for `.dash.js` component configs and
|
|
74150
74293
|
* return the parsed configs. Used when the widget registry's cached
|
|
@@ -74487,6 +74630,21 @@ async function prepareWidgetForPublish$1(appId, packageId, options = {}) {
|
|
|
74487
74630
|
}
|
|
74488
74631
|
}
|
|
74489
74632
|
|
|
74633
|
+
// 5b. Heal `.dash.js` source files that have duplicate
|
|
74634
|
+
// provider-type entries before we read configs / build the
|
|
74635
|
+
// manifest / zip. AI-generated configs occasionally double a
|
|
74636
|
+
// `{type:"..."}` entry; the runtime dedup makes it invisible
|
|
74637
|
+
// on the publisher's machine, but we don't want the dirty raw
|
|
74638
|
+
// text shipping to the registry. Mirrors the write-time dedup
|
|
74639
|
+
// in dash-electron's WidgetBuilderModal so older widgets
|
|
74640
|
+
// authored before that fix landed get cleaned at publish.
|
|
74641
|
+
const providerCleanupSummary = cleanupProvidersInWidgetPackage(widget.path);
|
|
74642
|
+
if (providerCleanupSummary.filesRewritten > 0) {
|
|
74643
|
+
console.log(
|
|
74644
|
+
`[widgetRegistry] Provider cleanup: rewrote ${providerCleanupSummary.filesRewritten} file(s), removed ${providerCleanupSummary.totalDropped} duplicate(s)`,
|
|
74645
|
+
);
|
|
74646
|
+
}
|
|
74647
|
+
|
|
74490
74648
|
// 6. Build manifest using the widget's component configs. The
|
|
74491
74649
|
// registry cache may be missing widgets (orphaned / locally-
|
|
74492
74650
|
// registered packages), so fall back to scanning the package's
|
|
@@ -75772,6 +75930,9 @@ const {
|
|
|
75772
75930
|
MCP_READ_RESOURCE,
|
|
75773
75931
|
MCP_SERVER_STATUS,
|
|
75774
75932
|
MCP_GET_CATALOG,
|
|
75933
|
+
MCP_GET_KNOWN_EXTERNAL,
|
|
75934
|
+
MCP_INSTALL_KNOWN_EXTERNAL_CONFIRM: MCP_INSTALL_KNOWN_EXTERNAL_CONFIRM$1,
|
|
75935
|
+
MCP_INSTALL_KNOWN_EXTERNAL_RESULT: MCP_INSTALL_KNOWN_EXTERNAL_RESULT$1,
|
|
75775
75936
|
MCP_RUN_AUTH,
|
|
75776
75937
|
} = events$8;
|
|
75777
75938
|
|
|
@@ -75868,6 +76029,45 @@ const mcpApi$2 = {
|
|
|
75868
76029
|
*/
|
|
75869
76030
|
getCatalog: () => ipcRenderer$i.invoke(MCP_GET_CATALOG),
|
|
75870
76031
|
|
|
76032
|
+
/**
|
|
76033
|
+
* getKnownExternalCatalog
|
|
76034
|
+
* Load the curated allow-list of MCP servers known to exist outside the
|
|
76035
|
+
* built-in catalog. The AI Widget Builder reads this to advertise
|
|
76036
|
+
* "you can install <X> via Add Custom MCP" and as the trust boundary
|
|
76037
|
+
* for the `install_known_mcp_server` dash MCP tool — only ids in this
|
|
76038
|
+
* list are installable via that path.
|
|
76039
|
+
*
|
|
76040
|
+
* @returns {Promise<{ success, servers } | { error, message, servers }>}
|
|
76041
|
+
*/
|
|
76042
|
+
getKnownExternalCatalog: () => ipcRenderer$i.invoke(MCP_GET_KNOWN_EXTERNAL),
|
|
76043
|
+
|
|
76044
|
+
/**
|
|
76045
|
+
* onInstallKnownExternalConfirm
|
|
76046
|
+
* Subscribe to install-confirm requests emitted by the dash MCP server
|
|
76047
|
+
* tool `install_known_mcp_server`. The renderer renders a confirmation
|
|
76048
|
+
* modal and replies with { confirmed, credentials } via
|
|
76049
|
+
* sendInstallKnownExternalResult().
|
|
76050
|
+
*
|
|
76051
|
+
* @param {(payload: { id, requestId, server }) => void} callback
|
|
76052
|
+
* @returns {() => void} cleanup
|
|
76053
|
+
*/
|
|
76054
|
+
onInstallKnownExternalConfirm: (callback) => {
|
|
76055
|
+
const handler = (_e, data) => callback(data);
|
|
76056
|
+
ipcRenderer$i.on(MCP_INSTALL_KNOWN_EXTERNAL_CONFIRM$1, handler);
|
|
76057
|
+
return () =>
|
|
76058
|
+
ipcRenderer$i.removeListener(MCP_INSTALL_KNOWN_EXTERNAL_CONFIRM$1, handler);
|
|
76059
|
+
},
|
|
76060
|
+
|
|
76061
|
+
/**
|
|
76062
|
+
* sendInstallKnownExternalResult
|
|
76063
|
+
* Reply to a confirm request with the user's decision + credentials.
|
|
76064
|
+
*
|
|
76065
|
+
* @param {string} requestId
|
|
76066
|
+
* @param {{ confirmed: boolean, credentials?: object, error?: string }} result
|
|
76067
|
+
*/
|
|
76068
|
+
sendInstallKnownExternalResult: (requestId, result) =>
|
|
76069
|
+
ipcRenderer$i.send(MCP_INSTALL_KNOWN_EXTERNAL_RESULT$1, { requestId, result }),
|
|
76070
|
+
|
|
75871
76071
|
/**
|
|
75872
76072
|
* runAuth
|
|
75873
76073
|
* Run a one-shot auth command for an MCP server (e.g., OAuth browser flow)
|
|
@@ -77176,7 +77376,7 @@ var mcpDashServerApi_1 = mcpDashServerApi$2;
|
|
|
77176
77376
|
* Call registerDashboardTools() during app startup (before or after server start).
|
|
77177
77377
|
*/
|
|
77178
77378
|
|
|
77179
|
-
const { registerTool: registerTool$
|
|
77379
|
+
const { registerTool: registerTool$6 } = mcpDashServerController_1;
|
|
77180
77380
|
const { dashboardTools } = toolDefinitions$1;
|
|
77181
77381
|
const {
|
|
77182
77382
|
handleListDashboards,
|
|
@@ -77207,7 +77407,7 @@ function registerDashboardTools$1() {
|
|
|
77207
77407
|
console.warn(`[dashboardTools] No handler found for tool: ${tool.name}`);
|
|
77208
77408
|
continue;
|
|
77209
77409
|
}
|
|
77210
|
-
registerTool$
|
|
77410
|
+
registerTool$6({
|
|
77211
77411
|
name: tool.name,
|
|
77212
77412
|
description: tool.description,
|
|
77213
77413
|
inputSchema: tool.inputSchema,
|
|
@@ -77228,7 +77428,7 @@ var dashboardTools_1 = { registerDashboardTools: registerDashboardTools$1 };
|
|
|
77228
77428
|
* Call registerWidgetTools() during app startup (before or after server start).
|
|
77229
77429
|
*/
|
|
77230
77430
|
|
|
77231
|
-
const { registerTool: registerTool$
|
|
77431
|
+
const { registerTool: registerTool$5 } = mcpDashServerController_1;
|
|
77232
77432
|
const { widgetTools } = toolDefinitions$1;
|
|
77233
77433
|
const {
|
|
77234
77434
|
handleAddWidget,
|
|
@@ -77259,7 +77459,7 @@ function registerWidgetTools$1() {
|
|
|
77259
77459
|
console.warn(`[widgetTools] No handler found for tool: ${tool.name}`);
|
|
77260
77460
|
continue;
|
|
77261
77461
|
}
|
|
77262
|
-
registerTool$
|
|
77462
|
+
registerTool$5({
|
|
77263
77463
|
name: tool.name,
|
|
77264
77464
|
description: tool.description,
|
|
77265
77465
|
inputSchema: tool.inputSchema,
|
|
@@ -77278,7 +77478,7 @@ var widgetTools_1 = { registerWidgetTools: registerWidgetTools$1 };
|
|
|
77278
77478
|
* Call registerThemeTools() during app startup (before or after server start).
|
|
77279
77479
|
*/
|
|
77280
77480
|
|
|
77281
|
-
const { registerTool: registerTool$
|
|
77481
|
+
const { registerTool: registerTool$4 } = mcpDashServerController_1;
|
|
77282
77482
|
const { themeTools } = toolDefinitions$1;
|
|
77283
77483
|
const {
|
|
77284
77484
|
handleListThemes,
|
|
@@ -77309,7 +77509,7 @@ function registerThemeTools$1() {
|
|
|
77309
77509
|
console.warn(`[themeTools] No handler found for tool: ${tool.name}`);
|
|
77310
77510
|
continue;
|
|
77311
77511
|
}
|
|
77312
|
-
registerTool$
|
|
77512
|
+
registerTool$4({
|
|
77313
77513
|
name: tool.name,
|
|
77314
77514
|
description: tool.description,
|
|
77315
77515
|
inputSchema: tool.inputSchema,
|
|
@@ -77328,18 +77528,18 @@ var themeTools_1 = { registerThemeTools: registerThemeTools$1 };
|
|
|
77328
77528
|
* Call registerProviderTools() during app startup (before or after server start).
|
|
77329
77529
|
*/
|
|
77330
77530
|
|
|
77331
|
-
const { registerTool: registerTool$
|
|
77531
|
+
const { registerTool: registerTool$3 } = mcpDashServerController_1;
|
|
77332
77532
|
const { providerTools } = toolDefinitions$1;
|
|
77333
77533
|
const {
|
|
77334
77534
|
handleListProviders,
|
|
77335
|
-
handleAddProvider,
|
|
77535
|
+
handleAddProvider: handleAddProvider$1,
|
|
77336
77536
|
handleRemoveProvider,
|
|
77337
77537
|
} = toolHandlers$1;
|
|
77338
77538
|
|
|
77339
77539
|
// Map tool names to handler functions
|
|
77340
77540
|
const handlerMap$4 = {
|
|
77341
77541
|
list_providers: handleListProviders,
|
|
77342
|
-
add_provider: handleAddProvider,
|
|
77542
|
+
add_provider: handleAddProvider$1,
|
|
77343
77543
|
remove_provider: handleRemoveProvider,
|
|
77344
77544
|
};
|
|
77345
77545
|
|
|
@@ -77353,7 +77553,7 @@ function registerProviderTools$1() {
|
|
|
77353
77553
|
console.warn(`[providerTools] No handler found for tool: ${tool.name}`);
|
|
77354
77554
|
continue;
|
|
77355
77555
|
}
|
|
77356
|
-
registerTool$
|
|
77556
|
+
registerTool$3({
|
|
77357
77557
|
name: tool.name,
|
|
77358
77558
|
description: tool.description,
|
|
77359
77559
|
inputSchema: tool.inputSchema,
|
|
@@ -77374,7 +77574,7 @@ var providerTools_1 = { registerProviderTools: registerProviderTools$1 };
|
|
|
77374
77574
|
* Call registerGuideTools() during app startup.
|
|
77375
77575
|
*/
|
|
77376
77576
|
|
|
77377
|
-
const { registerTool: registerTool$
|
|
77577
|
+
const { registerTool: registerTool$2 } = mcpDashServerController_1;
|
|
77378
77578
|
const { guideTools } = toolDefinitions$1;
|
|
77379
77579
|
const { handleGetSetupGuide } = toolHandlers$1;
|
|
77380
77580
|
|
|
@@ -77389,7 +77589,7 @@ function registerGuideTools$1() {
|
|
|
77389
77589
|
console.warn(`[guideTools] No handler found for tool: ${tool.name}`);
|
|
77390
77590
|
continue;
|
|
77391
77591
|
}
|
|
77392
|
-
registerTool$
|
|
77592
|
+
registerTool$2({
|
|
77393
77593
|
name: tool.name,
|
|
77394
77594
|
description: tool.description,
|
|
77395
77595
|
inputSchema: tool.inputSchema,
|
|
@@ -77408,7 +77608,7 @@ var guideTools_1 = { registerGuideTools: registerGuideTools$1 };
|
|
|
77408
77608
|
* Call registerLayoutTools() during app startup (before or after server start).
|
|
77409
77609
|
*/
|
|
77410
77610
|
|
|
77411
|
-
const { registerTool } = mcpDashServerController_1;
|
|
77611
|
+
const { registerTool: registerTool$1 } = mcpDashServerController_1;
|
|
77412
77612
|
const { layoutTools } = toolDefinitions$1;
|
|
77413
77613
|
const {
|
|
77414
77614
|
handleSetLayout,
|
|
@@ -77432,7 +77632,7 @@ function registerLayoutTools$1() {
|
|
|
77432
77632
|
console.warn(`[layoutTools] No handler found for tool: ${tool.name}`);
|
|
77433
77633
|
continue;
|
|
77434
77634
|
}
|
|
77435
|
-
registerTool({
|
|
77635
|
+
registerTool$1({
|
|
77436
77636
|
name: tool.name,
|
|
77437
77637
|
description: tool.description,
|
|
77438
77638
|
inputSchema: tool.inputSchema,
|
|
@@ -77444,6 +77644,194 @@ function registerLayoutTools$1() {
|
|
|
77444
77644
|
|
|
77445
77645
|
var layoutTools_1 = { registerLayoutTools: registerLayoutTools$1 };
|
|
77446
77646
|
|
|
77647
|
+
/**
|
|
77648
|
+
* installExternalMcpTool.js
|
|
77649
|
+
*
|
|
77650
|
+
* Registers the `install_known_mcp_server` tool on Dash's own MCP server.
|
|
77651
|
+
*
|
|
77652
|
+
* Why this tool exists:
|
|
77653
|
+
* The AI Widget Builder asks Claude to use MCP-first when a user requests
|
|
77654
|
+
* a widget for an external service. The built-in catalog covers 13 servers;
|
|
77655
|
+
* for anything else the AI consults the curated allow-list at
|
|
77656
|
+
* `electron/mcp/knownExternalMcpServers.json`. To keep the user in control
|
|
77657
|
+
* (npm install + spawning a child process is a non-trivial trust grant),
|
|
77658
|
+
* the actual install is gated by a renderer-side confirmation modal: this
|
|
77659
|
+
* tool emits an IPC event with the curated entry, waits for the user's
|
|
77660
|
+
* response, and only then routes through the existing add-provider flow.
|
|
77661
|
+
*
|
|
77662
|
+
* Trust boundary: the `id` argument MUST match an entry in
|
|
77663
|
+
* knownExternalMcpServers.json. Any other id is rejected before the
|
|
77664
|
+
* confirmation modal is even shown.
|
|
77665
|
+
*
|
|
77666
|
+
* Returns one of:
|
|
77667
|
+
* - { success: true, name } — installed
|
|
77668
|
+
* - { success: false, declined } — user clicked Cancel
|
|
77669
|
+
* - { success: false, error } — id not in allow-list, IPC timeout, or
|
|
77670
|
+
* install error from add-provider flow
|
|
77671
|
+
*/
|
|
77672
|
+
|
|
77673
|
+
const { BrowserWindow } = require$$0$1;
|
|
77674
|
+
const { registerTool } = mcpDashServerController_1;
|
|
77675
|
+
const mcpController$1 = mcpControllerExports;
|
|
77676
|
+
const {
|
|
77677
|
+
MCP_INSTALL_KNOWN_EXTERNAL_CONFIRM,
|
|
77678
|
+
MCP_INSTALL_KNOWN_EXTERNAL_RESULT,
|
|
77679
|
+
} = mcpEvents$1;
|
|
77680
|
+
const { handleAddProvider } = toolHandlers$1;
|
|
77681
|
+
const { ipcMain } = require$$0$1;
|
|
77682
|
+
|
|
77683
|
+
const CONFIRM_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes — user may stop to read
|
|
77684
|
+
|
|
77685
|
+
// Pending requests keyed by requestId so we can resolve when the result IPC arrives.
|
|
77686
|
+
const pendingRequests = new Map();
|
|
77687
|
+
|
|
77688
|
+
let resultListenerInstalled = false;
|
|
77689
|
+
function ensureResultListener() {
|
|
77690
|
+
if (resultListenerInstalled) return;
|
|
77691
|
+
resultListenerInstalled = true;
|
|
77692
|
+
ipcMain.on(MCP_INSTALL_KNOWN_EXTERNAL_RESULT, (_event, payload) => {
|
|
77693
|
+
const { requestId, result } = payload || {};
|
|
77694
|
+
const pending = pendingRequests.get(requestId);
|
|
77695
|
+
if (!pending) return;
|
|
77696
|
+
pendingRequests.delete(requestId);
|
|
77697
|
+
clearTimeout(pending.timer);
|
|
77698
|
+
pending.resolve(result || { confirmed: false });
|
|
77699
|
+
});
|
|
77700
|
+
}
|
|
77701
|
+
|
|
77702
|
+
function findEntryInAllowList(id) {
|
|
77703
|
+
const { servers } = mcpController$1.getKnownExternalCatalog();
|
|
77704
|
+
if (!Array.isArray(servers)) return null;
|
|
77705
|
+
return servers.find((s) => s && s.id === id) || null;
|
|
77706
|
+
}
|
|
77707
|
+
|
|
77708
|
+
function newRequestId() {
|
|
77709
|
+
return `mcp-install-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
77710
|
+
}
|
|
77711
|
+
|
|
77712
|
+
async function awaitUserConfirmation(server) {
|
|
77713
|
+
ensureResultListener();
|
|
77714
|
+
const requestId = newRequestId();
|
|
77715
|
+
return new Promise((resolve) => {
|
|
77716
|
+
const timer = setTimeout(() => {
|
|
77717
|
+
if (pendingRequests.has(requestId)) {
|
|
77718
|
+
pendingRequests.delete(requestId);
|
|
77719
|
+
resolve({ confirmed: false, error: "Install confirmation timed out" });
|
|
77720
|
+
}
|
|
77721
|
+
}, CONFIRM_TIMEOUT_MS);
|
|
77722
|
+
pendingRequests.set(requestId, { resolve, timer });
|
|
77723
|
+
|
|
77724
|
+
// Broadcast to all open windows; whichever has the modal mounted picks it up.
|
|
77725
|
+
const wins = BrowserWindow.getAllWindows();
|
|
77726
|
+
if (wins.length === 0) {
|
|
77727
|
+
pendingRequests.delete(requestId);
|
|
77728
|
+
clearTimeout(timer);
|
|
77729
|
+
resolve({ confirmed: false, error: "No window available" });
|
|
77730
|
+
return;
|
|
77731
|
+
}
|
|
77732
|
+
for (const win of wins) {
|
|
77733
|
+
if (!win.isDestroyed()) {
|
|
77734
|
+
win.webContents.send(MCP_INSTALL_KNOWN_EXTERNAL_CONFIRM, {
|
|
77735
|
+
requestId,
|
|
77736
|
+
id: server.id,
|
|
77737
|
+
server,
|
|
77738
|
+
});
|
|
77739
|
+
}
|
|
77740
|
+
}
|
|
77741
|
+
});
|
|
77742
|
+
}
|
|
77743
|
+
|
|
77744
|
+
function ok(payload) {
|
|
77745
|
+
return {
|
|
77746
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
77747
|
+
};
|
|
77748
|
+
}
|
|
77749
|
+
|
|
77750
|
+
function fail(payload) {
|
|
77751
|
+
return {
|
|
77752
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
77753
|
+
isError: true,
|
|
77754
|
+
};
|
|
77755
|
+
}
|
|
77756
|
+
|
|
77757
|
+
async function handleInstallKnownMcpServer({ id, name }) {
|
|
77758
|
+
if (!id || typeof id !== "string") {
|
|
77759
|
+
return fail({ success: false, error: "id is required" });
|
|
77760
|
+
}
|
|
77761
|
+
const server = findEntryInAllowList(id);
|
|
77762
|
+
if (!server) {
|
|
77763
|
+
return fail({
|
|
77764
|
+
success: false,
|
|
77765
|
+
error: `id "${id}" is not in the known-external MCP allow-list. The user can only install servers explicitly listed in knownExternalMcpServers.json.`,
|
|
77766
|
+
});
|
|
77767
|
+
}
|
|
77768
|
+
|
|
77769
|
+
const decision = await awaitUserConfirmation(server);
|
|
77770
|
+
if (!decision || !decision.confirmed) {
|
|
77771
|
+
if (decision?.error) {
|
|
77772
|
+
return fail({ success: false, error: decision.error });
|
|
77773
|
+
}
|
|
77774
|
+
return ok({ success: false, declined: true });
|
|
77775
|
+
}
|
|
77776
|
+
|
|
77777
|
+
// Route through the existing add-provider tool handler. The renderer
|
|
77778
|
+
// collected credentials in its modal — we trust those to match the
|
|
77779
|
+
// curated `credentialSchema`.
|
|
77780
|
+
const providerName = (name && name.trim()) || server.name || server.id;
|
|
77781
|
+
try {
|
|
77782
|
+
const addResult = await handleAddProvider({
|
|
77783
|
+
name: providerName,
|
|
77784
|
+
type: server.id,
|
|
77785
|
+
providerClass: "mcp",
|
|
77786
|
+
credentials: decision.credentials || {},
|
|
77787
|
+
mcpConfig: server.mcpConfig,
|
|
77788
|
+
});
|
|
77789
|
+
if (addResult?.isError) {
|
|
77790
|
+
// Surface add-provider's error text verbatim.
|
|
77791
|
+
return addResult;
|
|
77792
|
+
}
|
|
77793
|
+
return ok({ success: true, name: providerName, type: server.id });
|
|
77794
|
+
} catch (err) {
|
|
77795
|
+
return fail({ success: false, error: err.message });
|
|
77796
|
+
}
|
|
77797
|
+
}
|
|
77798
|
+
|
|
77799
|
+
const installExternalMcpToolDef = {
|
|
77800
|
+
name: "install_known_mcp_server",
|
|
77801
|
+
description:
|
|
77802
|
+
"Install an MCP server from Dash's curated known-external allow-list. The user is shown a confirmation modal before any install runs. Use this when the user asks for a widget that needs a service NOT in the built-in MCP catalog but IS in the known-external list. Returns { success, name } on install, { success: false, declined: true } if the user cancels, or an error if the id isn't allow-listed.",
|
|
77803
|
+
inputSchema: {
|
|
77804
|
+
type: "object",
|
|
77805
|
+
properties: {
|
|
77806
|
+
id: {
|
|
77807
|
+
type: "string",
|
|
77808
|
+
description:
|
|
77809
|
+
"The id of the server in the known-external catalog (e.g. 'trello', 'asana', 'stripe').",
|
|
77810
|
+
},
|
|
77811
|
+
name: {
|
|
77812
|
+
type: "string",
|
|
77813
|
+
description:
|
|
77814
|
+
"Optional display name for the resulting provider entry. Defaults to the catalog entry's name.",
|
|
77815
|
+
},
|
|
77816
|
+
},
|
|
77817
|
+
required: ["id"],
|
|
77818
|
+
},
|
|
77819
|
+
};
|
|
77820
|
+
|
|
77821
|
+
function registerInstallKnownMcpServerTool$1() {
|
|
77822
|
+
registerTool({
|
|
77823
|
+
...installExternalMcpToolDef,
|
|
77824
|
+
handler: handleInstallKnownMcpServer,
|
|
77825
|
+
});
|
|
77826
|
+
console.log("[installExternalMcpTool] Registered install_known_mcp_server");
|
|
77827
|
+
}
|
|
77828
|
+
|
|
77829
|
+
var installExternalMcpTool = {
|
|
77830
|
+
registerInstallKnownMcpServerTool: registerInstallKnownMcpServerTool$1,
|
|
77831
|
+
// Exported for tests
|
|
77832
|
+
handleInstallKnownMcpServer,
|
|
77833
|
+
};
|
|
77834
|
+
|
|
77447
77835
|
/**
|
|
77448
77836
|
* resourceDefinitions.js
|
|
77449
77837
|
*
|
|
@@ -78414,6 +78802,9 @@ const { registerThemeTools } = themeTools_1;
|
|
|
78414
78802
|
const { registerProviderTools } = providerTools_1;
|
|
78415
78803
|
const { registerGuideTools } = guideTools_1;
|
|
78416
78804
|
const { registerLayoutTools } = layoutTools_1;
|
|
78805
|
+
const {
|
|
78806
|
+
registerInstallKnownMcpServerTool,
|
|
78807
|
+
} = installExternalMcpTool;
|
|
78417
78808
|
const { registerResources } = resources;
|
|
78418
78809
|
const { registerPrompts } = promptRegistration;
|
|
78419
78810
|
registerDashboardTools();
|
|
@@ -78422,6 +78813,7 @@ registerThemeTools();
|
|
|
78422
78813
|
registerProviderTools();
|
|
78423
78814
|
registerGuideTools();
|
|
78424
78815
|
registerLayoutTools();
|
|
78816
|
+
registerInstallKnownMcpServerTool();
|
|
78425
78817
|
registerResources();
|
|
78426
78818
|
registerPrompts();
|
|
78427
78819
|
|