dsh-edge 0.15.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -274,23 +274,43 @@ window.__ModuleLoader__.load({
274
274
  return health.ok === true && health.service === "dsh-edge" && health.status === "ready" && (health.shell === "just-bash-direct" || health.shell === "just-bash-isolated") && health.storage === "durable-object-sqlite-vfs" && typeof health.deploymentId === "string" && typeof health.version === "string" && typeof health.upstreamVersion === "string";
275
275
  }
276
276
  /** Lazy, fail-soft controller for the Edge deployment settings page. */
277
- var EdgeSettingsController = class {
277
+ var EdgeSettingsController = class EdgeSettingsController {
278
278
  io;
279
279
  /** Observable settings state consumed by the client runtime. */
280
280
  store = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)({
281
281
  status: "idle",
282
282
  copied: false,
283
- signingOut: false
283
+ signingOut: false,
284
+ approvalMode: "never",
285
+ approvalSaving: false,
286
+ approvalSaved: false,
287
+ mcpServers: [],
288
+ mcpLoaded: false,
289
+ mcpSaving: false
284
290
  });
285
291
  loadGeneration = 0;
292
+ approvalGeneration = 0;
293
+ mcpGeneration = 0;
294
+ oauthMessageHandler;
286
295
  constructor(io) {
287
296
  this.io = io;
297
+ this.oauthMessageHandler = (e) => {
298
+ if (e.data?.type === "mcp-oauth-complete") this.refreshMcpServers();
299
+ };
300
+ globalThis.addEventListener?.("message", this.oauthMessageHandler);
301
+ }
302
+ dispose() {
303
+ if (this.oauthMessageHandler !== void 0) {
304
+ globalThis.removeEventListener?.("message", this.oauthMessageHandler);
305
+ this.oauthMessageHandler = void 0;
306
+ }
288
307
  }
289
308
  /** Load the current deployment projection without affecting owner-session state. */
290
309
  async load() {
291
310
  const generation = ++this.loadGeneration;
292
311
  this.store.update((state) => {
293
312
  state.status = "loading";
313
+ state.mcpLoaded = false;
294
314
  delete state.error;
295
315
  });
296
316
  try {
@@ -308,6 +328,36 @@ window.__ModuleLoader__.load({
308
328
  delete state.latestVersion;
309
329
  delete state.error;
310
330
  });
331
+ const approvalGen = ++this.approvalGeneration;
332
+ try {
333
+ const approvalResponse = await this.io.fetch("/api/approval-mode", { credentials: "same-origin" });
334
+ if (approvalGen === this.approvalGeneration) {
335
+ if (approvalResponse.ok) {
336
+ const data = await approvalResponse.json();
337
+ if (approvalGen === this.approvalGeneration && (data.mode === "ask" || data.mode === "never")) this.store.update((state) => {
338
+ state.approvalMode = data.mode;
339
+ delete state.approvalError;
340
+ });
341
+ } else this.store.update((state) => {
342
+ state.approvalError = `HTTP ${String(approvalResponse.status)}`;
343
+ });
344
+ }
345
+ } catch {
346
+ if (approvalGen === this.approvalGeneration) this.store.update((state) => {
347
+ state.approvalError = "Could not load approval setting.";
348
+ });
349
+ }
350
+ const mcpGen = ++this.mcpGeneration;
351
+ try {
352
+ const mcpResponse = await this.io.fetch("/api/mcp-servers", { credentials: "same-origin" });
353
+ if (mcpGen === this.mcpGeneration && mcpResponse.ok) {
354
+ const data = await mcpResponse.json();
355
+ if (mcpGen === this.mcpGeneration && Array.isArray(data.servers)) this.store.update((state) => {
356
+ state.mcpServers = data.servers;
357
+ state.mcpLoaded = true;
358
+ });
359
+ }
360
+ } catch {}
311
361
  let latestVersion;
312
362
  try {
313
363
  const releaseResponse = await this.io.fetch(`https://registry.npmjs.org/dsh-edge/${channel}`, { signal: AbortSignal.timeout(5e3) });
@@ -330,7 +380,234 @@ window.__ModuleLoader__.load({
330
380
  });
331
381
  }
332
382
  }
333
- /** Copy the matching channel upgrade command without affecting deployment state. */
383
+ async setApprovalMode(mode) {
384
+ this.approvalGeneration++;
385
+ this.store.update((state) => {
386
+ state.approvalSaving = true;
387
+ state.approvalSaved = false;
388
+ delete state.approvalError;
389
+ });
390
+ try {
391
+ const response = await this.io.fetch("/api/approval-mode", {
392
+ method: "PUT",
393
+ credentials: "same-origin",
394
+ headers: { "content-type": "application/json" },
395
+ body: JSON.stringify({ mode })
396
+ });
397
+ if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
398
+ this.store.update((state) => {
399
+ state.approvalMode = mode;
400
+ state.approvalSaving = false;
401
+ state.approvalSaved = true;
402
+ });
403
+ } catch (error) {
404
+ this.store.update((state) => {
405
+ state.approvalSaving = false;
406
+ state.approvalError = messageOf(error);
407
+ });
408
+ }
409
+ }
410
+ async refreshMcpServers() {
411
+ const mcpGen = ++this.mcpGeneration;
412
+ try {
413
+ const response = await this.io.fetch("/api/mcp-servers", { credentials: "same-origin" });
414
+ if (mcpGen === this.mcpGeneration && response.ok) {
415
+ const data = await response.json();
416
+ if (mcpGen === this.mcpGeneration && Array.isArray(data.servers)) this.store.update((state) => {
417
+ state.mcpServers = data.servers;
418
+ state.mcpLoaded = true;
419
+ });
420
+ }
421
+ } catch {}
422
+ }
423
+ static stripServerManagedFields(servers) {
424
+ return servers.map(({ serverName, url, auth, toolPolicy, toolCallTimeoutMs }) => ({
425
+ serverName,
426
+ url,
427
+ ...auth !== void 0 ? { auth } : {},
428
+ ...toolPolicy !== void 0 ? { toolPolicy } : {},
429
+ ...toolCallTimeoutMs !== void 0 ? { toolCallTimeoutMs } : {}
430
+ }));
431
+ }
432
+ async saveMcpServers(servers) {
433
+ this.mcpGeneration++;
434
+ this.store.update((state) => {
435
+ state.mcpSaving = true;
436
+ delete state.mcpError;
437
+ });
438
+ try {
439
+ const response = await this.io.fetch("/api/mcp-servers", {
440
+ method: "PUT",
441
+ credentials: "same-origin",
442
+ headers: { "content-type": "application/json" },
443
+ body: JSON.stringify({ servers: EdgeSettingsController.stripServerManagedFields(servers) })
444
+ });
445
+ if (!response.ok) {
446
+ const data = await response.json().catch(() => ({}));
447
+ throw new Error(data.error ?? `HTTP ${String(response.status)}`);
448
+ }
449
+ const saved = (await response.json()).servers ?? servers;
450
+ this.store.update((state) => {
451
+ state.mcpServers = saved;
452
+ });
453
+ const probeErrors = [];
454
+ for (const s of saved) {
455
+ if (s.auth?.type === "oauth") continue;
456
+ try {
457
+ const probeRes = await this.io.fetch(`/api/mcp-servers/${encodeURIComponent(s.serverName)}/probe`, {
458
+ method: "POST",
459
+ credentials: "same-origin"
460
+ });
461
+ if (!probeRes.ok) {
462
+ const data = await probeRes.json().catch(() => ({}));
463
+ probeErrors.push(`${s.serverName}: ${data.error ?? "probe failed"}`);
464
+ }
465
+ } catch {
466
+ probeErrors.push(`${s.serverName}: network error`);
467
+ }
468
+ }
469
+ await this.refreshMcpServers();
470
+ this.store.update((state) => {
471
+ state.mcpSaving = false;
472
+ });
473
+ return true;
474
+ } catch (error) {
475
+ this.store.update((state) => {
476
+ state.mcpSaving = false;
477
+ state.mcpError = messageOf(error);
478
+ });
479
+ return false;
480
+ }
481
+ }
482
+ async setMcpToolPolicy(serverName, mode) {
483
+ const updated = this.store.getSnapshot().mcpServers.map((s) => s.serverName === serverName ? {
484
+ ...s,
485
+ toolPolicy: { mode }
486
+ } : s);
487
+ try {
488
+ const response = await this.io.fetch("/api/mcp-servers", {
489
+ method: "PUT",
490
+ credentials: "same-origin",
491
+ headers: { "content-type": "application/json" },
492
+ body: JSON.stringify({ servers: EdgeSettingsController.stripServerManagedFields(updated) })
493
+ });
494
+ if (!response.ok) {
495
+ const data = await response.json().catch(() => ({}));
496
+ this.store.update((state) => {
497
+ state.mcpError = data.error ?? "Policy update failed";
498
+ });
499
+ return false;
500
+ }
501
+ const result = await response.json();
502
+ if (Array.isArray(result.servers)) this.store.update((state) => {
503
+ state.mcpServers = result.servers;
504
+ delete state.mcpError;
505
+ });
506
+ return true;
507
+ } catch {
508
+ return false;
509
+ }
510
+ }
511
+ async probeMcpServer(serverName) {
512
+ try {
513
+ if (!(await this.io.fetch(`/api/mcp-servers/${encodeURIComponent(serverName)}/probe`, {
514
+ method: "POST",
515
+ credentials: "same-origin"
516
+ })).ok) {
517
+ await this.refreshMcpServers();
518
+ return false;
519
+ }
520
+ await this.refreshMcpServers();
521
+ return true;
522
+ } catch {
523
+ await this.refreshMcpServers().catch(() => {});
524
+ return false;
525
+ }
526
+ }
527
+ async getMcpTools(serverName) {
528
+ try {
529
+ const response = await this.io.fetch(`/api/mcp-servers/${encodeURIComponent(serverName)}/tools`, { credentials: "same-origin" });
530
+ if (!response.ok) return [];
531
+ return (await response.json()).tools ?? [];
532
+ } catch {
533
+ return [];
534
+ }
535
+ }
536
+ async saveMcpToken(serverName, token) {
537
+ try {
538
+ if (!(await this.io.fetch(`/api/mcp-servers/${encodeURIComponent(serverName)}/token`, {
539
+ method: "PUT",
540
+ credentials: "same-origin",
541
+ headers: { "content-type": "application/json" },
542
+ body: JSON.stringify({ token })
543
+ })).ok) return false;
544
+ await this.io.fetch(`/api/mcp-servers/${encodeURIComponent(serverName)}/probe`, {
545
+ method: "POST",
546
+ credentials: "same-origin"
547
+ }).catch(() => {});
548
+ await this.refreshMcpServers();
549
+ return true;
550
+ } catch {
551
+ return false;
552
+ }
553
+ }
554
+ async startOAuthConnect(serverName, serverUrl) {
555
+ const redirectUri = `${globalThis.location?.origin ?? ""}/api/mcp/oauth/callback`;
556
+ try {
557
+ const response = await this.io.fetch("/api/mcp/oauth/start", {
558
+ method: "POST",
559
+ credentials: "same-origin",
560
+ headers: { "content-type": "application/json" },
561
+ body: JSON.stringify({
562
+ serverName,
563
+ serverUrl,
564
+ redirectUri
565
+ })
566
+ });
567
+ if (!response.ok) {
568
+ const data = await response.json().catch(() => ({}));
569
+ this.store.update((state) => {
570
+ state.mcpError = data.error ?? "OAuth start failed";
571
+ });
572
+ return;
573
+ }
574
+ return (await response.json()).authorizationUrl;
575
+ } catch (error) {
576
+ this.store.update((state) => {
577
+ state.mcpError = messageOf(error);
578
+ });
579
+ return;
580
+ }
581
+ }
582
+ async completeOAuthConnect(code, state) {
583
+ try {
584
+ const response = await this.io.fetch("/api/mcp/oauth/complete", {
585
+ method: "POST",
586
+ credentials: "same-origin",
587
+ headers: { "content-type": "application/json" },
588
+ body: JSON.stringify({
589
+ code,
590
+ state
591
+ })
592
+ });
593
+ if (!response.ok) {
594
+ const data = await response.json().catch(() => ({}));
595
+ this.store.update((state) => {
596
+ state.mcpError = data.error ?? "OAuth complete failed";
597
+ });
598
+ return false;
599
+ }
600
+ return true;
601
+ } catch {
602
+ return false;
603
+ }
604
+ }
605
+ async clearMcpToken(serverName) {
606
+ await this.io.fetch(`/api/mcp-servers/${encodeURIComponent(serverName)}/token`, {
607
+ method: "DELETE",
608
+ credentials: "same-origin"
609
+ }).catch(() => {});
610
+ }
334
611
  async copyUpgrade() {
335
612
  try {
336
613
  const version = this.store.getSnapshot().health?.version ?? "0.0.0";
@@ -370,7 +647,7 @@ window.__ModuleLoader__.load({
370
647
  };
371
648
  //#endregion
372
649
  //#region \0dsh-edge-css:src/client/EdgeSettingsSection.module.css.mjs
373
- const css = "._68Y4qa_section{gap:20px;max-width:720px;display:grid}._68Y4qa_section header h2,._68Y4qa_card h3{color:var(--dsw-alias-text-primary);margin:0}._68Y4qa_section header p,._68Y4qa_card p{color:var(--dsw-alias-label-secondary)}._68Y4qa_card{border:1px solid var(--dsw-alias-border-subtle);border-radius:12px;padding:18px}._68Y4qa_card dl{margin:12px 0}._68Y4qa_row{border-bottom:1px solid var(--dsw-alias-border-secondary);grid-template-columns:minmax(150px,1fr) minmax(0,2fr);gap:16px;padding:8px 0;display:grid}._68Y4qa_row:last-child{border-bottom:0}._68Y4qa_row dt{color:var(--dsw-alias-label-secondary)}._68Y4qa_row dd{overflow-wrap:anywhere;color:var(--dsw-alias-text-primary);margin:0}._68Y4qa_actions{flex-wrap:wrap;align-items:center;gap:12px;display:flex}._68Y4qa_actions a{color:var(--dsw-alias-brand-primary)}._68Y4qa_status{align-items:center;gap:8px;display:flex}._68Y4qa_notice{color:var(--dsw-alias-label-secondary)}._68Y4qa_error{color:var(--dsw-alias-state-error-primary)!important}@media (width<=560px){._68Y4qa_row{grid-template-columns:1fr;gap:4px}}";
650
+ const css = "._68Y4qa_section{gap:20px;max-width:720px;display:grid}._68Y4qa_section header h2,._68Y4qa_card h3{color:var(--dsw-alias-text-primary);margin:0}._68Y4qa_section header p,._68Y4qa_card p{color:var(--dsw-alias-label-secondary)}._68Y4qa_card{border:1px solid var(--dsw-alias-border-subtle);border-radius:12px;padding:18px}._68Y4qa_card dl{margin:12px 0}._68Y4qa_row{border-bottom:1px solid var(--dsw-alias-border-secondary);grid-template-columns:minmax(150px,1fr) minmax(0,2fr);gap:16px;padding:8px 0;display:grid}._68Y4qa_row:last-child{border-bottom:0}._68Y4qa_row dt{color:var(--dsw-alias-label-secondary)}._68Y4qa_row dd{overflow-wrap:anywhere;color:var(--dsw-alias-text-primary);margin:0}._68Y4qa_actions{flex-wrap:wrap;align-items:center;gap:12px;display:flex}._68Y4qa_actions a{color:var(--dsw-alias-brand-primary)}._68Y4qa_status{align-items:center;gap:8px;display:flex}._68Y4qa_notice{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));font-size:13px}._68Y4qa_error{word-break:break-word;overflow-wrap:anywhere;font-size:13px;color:var(--dsw-alias-state-error-primary)!important}._68Y4qa_select{border:.5px solid var(--dsw-alias-border-l4,var(--dsw-alias-border-subtle));background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-background-primary));color:var(--dsw-alias-label-primary,var(--dsw-alias-text-primary));height:34px;font:inherit;appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23999' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");background-position:right 10px center;background-repeat:no-repeat;border-radius:8px;padding:0 28px 0 10px;font-size:13px;line-height:1.5}._68Y4qa_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}._68Y4qa_input{border:.5px solid var(--dsw-alias-border-l4,var(--dsw-alias-border-subtle));background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-background-primary));color:var(--dsw-alias-label-primary,var(--dsw-alias-text-primary));height:34px;font:inherit;box-sizing:border-box;border-radius:8px;width:100%;padding:0 12px;font-size:13px;line-height:1.5}._68Y4qa_input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}._68Y4qa_mcpList{flex-direction:column;gap:10px;margin:0;padding:0;list-style:none;display:flex}._68Y4qa_mcpCard{border:.5px solid var(--dsw-alias-border-l4,var(--dsw-alias-border-subtle));background:var(--dsw-alias-bg-layer-3,transparent);border-radius:16px;list-style:none;transition:border-color .16s,background .16s}._68Y4qa_mcpCard:hover{border-color:var(--dsw-alias-label-dimmed,#bbb)}._68Y4qa_mcpCardOpen{background:var(--dsw-alias-bg-layer-2,transparent);border-color:var(--dsw-alias-label-dimmed,#bbb)}._68Y4qa_mcpCardHeader{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}._68Y4qa_mcpCardHeader:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}._68Y4qa_mcpAvatar{background:var(--dsw-alias-brand-background,#eef2fe);width:32px;height:32px;color:var(--dsw-alias-brand-primary);border-radius:8px;flex-shrink:0;justify-content:center;align-items:center;font-size:14px;font-weight:600;display:flex}._68Y4qa_mcpCardTitleGroup{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}._68Y4qa_mcpCardName{color:var(--dsw-alias-label-primary,var(--dsw-alias-text-primary));margin:0;font-size:15px;font-weight:600;line-height:1.4}._68Y4qa_mcpCardSub{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));align-items:center;gap:8px;margin:0;font-size:13px;line-height:1.5;display:flex}._68Y4qa_mcpChevron{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));flex:none;transition:transform .16s}._68Y4qa_mcpChevronOpen{transform:rotate(180deg)}._68Y4qa_mcpChevron svg{display:block}._68Y4qa_mcpBadge{border-radius:4px;padding:1px 6px;font-size:11px;font-weight:500}._68Y4qa_mcpBadge[data-status=connected]{background:var(--dsw-alias-state-success-background,#ecfdf3);color:var(--dsw-alias-state-success-primary,#16a34a)}._68Y4qa_mcpBadge[data-status=error]{background:var(--dsw-alias-state-error-background,#fff0f0);color:var(--dsw-alias-state-error-primary,#dc2626)}._68Y4qa_mcpBadge[data-status=needs_reauth]{background:var(--dsw-alias-state-warning-background,#fff8e6);color:var(--dsw-alias-state-warning-primary,#d97706)}._68Y4qa_mcpBadge[data-status=unknown]{background:var(--dsw-alias-background-secondary,#f3f4f6);color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary))}._68Y4qa_mcpCardBody{border-top:.5px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-secondary));gap:12px;min-width:0;margin:0 16px;padding:12px 0 8px;display:grid}._68Y4qa_mcpFieldRow{grid-template-columns:140px 1fr;align-items:center;gap:12px;display:grid}._68Y4qa_mcpFieldLabel{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));font-size:13px;line-height:1.5}._68Y4qa_mcpFieldValue{color:var(--dsw-alias-label-primary,var(--dsw-alias-text-primary));font-size:13px;line-height:1.5}._68Y4qa_mcpCardSuccess{color:var(--dsw-alias-state-success-primary,#16a34a);margin:0;padding:4px 0;font-size:12px;line-height:1.4}._68Y4qa_mcpCardError{color:var(--dsw-alias-state-error-primary,#dc2626);margin:0;padding:4px 0;font-size:12px;line-height:1.4}._68Y4qa_mcpCardActions{border-top:.5px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-secondary));justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}._68Y4qa_mcpAddTrigger{appearance:none;width:100%;font:inherit;cursor:pointer;border:.5px dashed var(--dsw-alias-border-l4,var(--dsw-alias-border-subtle));color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));background:0 0;border-radius:16px;justify-content:center;align-items:center;gap:6px;padding:14px 16px;font-size:13px;line-height:1.5;transition:border-color .16s,color .16s;display:flex}._68Y4qa_mcpAddTrigger:hover{border-color:var(--dsw-alias-label-dimmed,#bbb);color:var(--dsw-alias-label-primary,var(--dsw-alias-text-primary))}._68Y4qa_mcpAddTrigger:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}._68Y4qa_mcpToolList{flex-direction:column;gap:6px;display:flex}._68Y4qa_mcpToolItems{flex-direction:column;gap:0;min-width:0;max-height:300px;margin:4px 0 0;padding:0;list-style:none;display:flex;overflow-y:auto}._68Y4qa_mcpToolItem{border-bottom:.5px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-secondary));flex-wrap:wrap;align-items:baseline;gap:4px 8px;padding:6px 0;display:flex}._68Y4qa_mcpToolItem:last-child{border-bottom:none}._68Y4qa_mcpToolName{font-family:var(--ds-font-family-code,monospace);color:var(--dsw-alias-label-primary,var(--dsw-alias-text-primary));font-size:12px}._68Y4qa_mcpToolDesc{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));width:100%;font-size:12px;line-height:1.4}._68Y4qa_mcpAddForm{border:.5px solid var(--dsw-alias-border-l4,var(--dsw-alias-border-subtle));border-radius:16px;gap:14px;padding:16px;display:grid}._68Y4qa_mcpFormRow{grid-template-columns:1fr 1fr;gap:10px;display:grid}._68Y4qa_mcpFormActions{justify-content:flex-end;gap:8px;display:flex}@media (width<=560px){._68Y4qa_row{grid-template-columns:1fr;gap:4px}._68Y4qa_mcpFormRow{grid-template-columns:1fr}._68Y4qa_mcpFieldRow{grid-template-columns:1fr;gap:4px}}";
374
651
  const tagId = "dsh-edge-client-ui/src/client/EdgeSettingsSection.module.css";
375
652
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
376
653
  const tag = document.createElement("style");
@@ -383,9 +660,38 @@ window.__ModuleLoader__.load({
383
660
  "actions": "_68Y4qa_actions",
384
661
  "card": "_68Y4qa_card",
385
662
  "error": "_68Y4qa_error",
663
+ "input": "_68Y4qa_input",
664
+ "mcpAddForm": "_68Y4qa_mcpAddForm",
665
+ "mcpAddTrigger": "_68Y4qa_mcpAddTrigger",
666
+ "mcpAvatar": "_68Y4qa_mcpAvatar",
667
+ "mcpBadge": "_68Y4qa_mcpBadge",
668
+ "mcpCard": "_68Y4qa_mcpCard",
669
+ "mcpCardActions": "_68Y4qa_mcpCardActions",
670
+ "mcpCardBody": "_68Y4qa_mcpCardBody",
671
+ "mcpCardError": "_68Y4qa_mcpCardError",
672
+ "mcpCardHeader": "_68Y4qa_mcpCardHeader",
673
+ "mcpCardName": "_68Y4qa_mcpCardName",
674
+ "mcpCardOpen": "_68Y4qa_mcpCardOpen",
675
+ "mcpCardSub": "_68Y4qa_mcpCardSub",
676
+ "mcpCardSuccess": "_68Y4qa_mcpCardSuccess",
677
+ "mcpCardTitleGroup": "_68Y4qa_mcpCardTitleGroup",
678
+ "mcpChevron": "_68Y4qa_mcpChevron",
679
+ "mcpChevronOpen": "_68Y4qa_mcpChevronOpen",
680
+ "mcpFieldLabel": "_68Y4qa_mcpFieldLabel",
681
+ "mcpFieldRow": "_68Y4qa_mcpFieldRow",
682
+ "mcpFieldValue": "_68Y4qa_mcpFieldValue",
683
+ "mcpFormActions": "_68Y4qa_mcpFormActions",
684
+ "mcpFormRow": "_68Y4qa_mcpFormRow",
685
+ "mcpList": "_68Y4qa_mcpList",
686
+ "mcpToolDesc": "_68Y4qa_mcpToolDesc",
687
+ "mcpToolItem": "_68Y4qa_mcpToolItem",
688
+ "mcpToolItems": "_68Y4qa_mcpToolItems",
689
+ "mcpToolList": "_68Y4qa_mcpToolList",
690
+ "mcpToolName": "_68Y4qa_mcpToolName",
386
691
  "notice": "_68Y4qa_notice",
387
692
  "row": "_68Y4qa_row",
388
693
  "section": "_68Y4qa_section",
694
+ "select": "_68Y4qa_select",
389
695
  "status": "_68Y4qa_status"
390
696
  };
391
697
  //#endregion
@@ -397,7 +703,7 @@ window.__ModuleLoader__.load({
397
703
  });
398
704
  }
399
705
  function EdgeSettingsSection(props) {
400
- const { useEdgeSettings, load, copyUpgrade, signOut, t } = props;
706
+ const { useEdgeSettings, load, copyUpgrade, signOut, setApprovalMode, t } = props;
401
707
  (0, react.useEffect)(() => {
402
708
  load();
403
709
  }, [load]);
@@ -494,6 +800,44 @@ window.__ModuleLoader__.load({
494
800
  children: [
495
801
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: t("title") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("intro") })] }),
496
802
  deploymentDetails,
803
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
804
+ className: EdgeSettingsSection_module_css_default.card,
805
+ "aria-labelledby": "edge-approval-title",
806
+ children: [
807
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
808
+ id: "edge-approval-title",
809
+ children: t("toolPermissions")
810
+ }),
811
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dl", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
812
+ label: t("approvalMode"),
813
+ value: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
814
+ className: EdgeSettingsSection_module_css_default.select,
815
+ value: state.approvalMode,
816
+ disabled: state.approvalSaving || state.status !== "ready",
817
+ "aria-label": t("approvalMode"),
818
+ onChange: (e) => {
819
+ setApprovalMode(e.target.value);
820
+ },
821
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
822
+ value: "ask",
823
+ children: t("approvalAsk")
824
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
825
+ value: "never",
826
+ children: t("approvalNever")
827
+ })]
828
+ })
829
+ }) }),
830
+ state.approvalSaved ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
831
+ className: EdgeSettingsSection_module_css_default.status,
832
+ children: t("approvalSaved")
833
+ }) : null,
834
+ state.approvalError !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
835
+ className: EdgeSettingsSection_module_css_default.error,
836
+ role: "alert",
837
+ children: t("approvalError")
838
+ }) : null
839
+ ]
840
+ }),
497
841
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
498
842
  className: EdgeSettingsSection_module_css_default.card,
499
843
  "aria-labelledby": "edge-owner-title",
@@ -523,6 +867,467 @@ window.__ModuleLoader__.load({
523
867
  });
524
868
  }
525
869
  //#endregion
870
+ //#region ../../../packages/client/ui-edge/src/client/EdgeMcpSection.tsx
871
+ function ChevronIcon({ expanded }) {
872
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
873
+ className: `${EdgeSettingsSection_module_css_default.mcpChevron}${expanded ? ` ${EdgeSettingsSection_module_css_default.mcpChevronOpen}` : ""}`,
874
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
875
+ width: "16",
876
+ height: "16",
877
+ viewBox: "0 0 16 16",
878
+ fill: "none",
879
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
880
+ d: "M4 6l4 4 4-4",
881
+ stroke: "currentColor",
882
+ strokeWidth: "1.5",
883
+ strokeLinecap: "round",
884
+ strokeLinejoin: "round"
885
+ })
886
+ })
887
+ });
888
+ }
889
+ function McpConnectorCard({ server: s, saving, disabled, expanded, onToggle, onRemove, onSetToolPolicy, onProbe, onGetTools, onOAuthConnect, connecting, t }) {
890
+ const hasTools = s.toolCount !== void 0 && s.toolCount > 0;
891
+ const [probing, setProbing] = (0, react.useState)(false);
892
+ const [tools, setTools] = (0, react.useState)(null);
893
+ const [toolsLoading, setToolsLoading] = (0, react.useState)(false);
894
+ (0, react.useEffect)(() => {
895
+ if (expanded && hasTools && tools === null && !toolsLoading) {
896
+ setToolsLoading(true);
897
+ onGetTools().then((t) => {
898
+ setTools(t);
899
+ setToolsLoading(false);
900
+ });
901
+ }
902
+ if (!expanded) setTools(null);
903
+ }, [expanded, hasTools]);
904
+ const authLabel = s.auth?.type === "oauth" ? "OAuth 2.1" : s.auth?.type === "bearer" ? "Bearer Token" : t("mcpAuthNone");
905
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
906
+ className: `${EdgeSettingsSection_module_css_default.mcpCard}${expanded ? ` ${EdgeSettingsSection_module_css_default.mcpCardOpen}` : ""}`,
907
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
908
+ type: "button",
909
+ className: EdgeSettingsSection_module_css_default.mcpCardHeader,
910
+ "aria-expanded": expanded,
911
+ onClick: onToggle,
912
+ children: [
913
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
914
+ className: EdgeSettingsSection_module_css_default.mcpAvatar,
915
+ children: s.serverName.charAt(0).toUpperCase()
916
+ }),
917
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
918
+ className: EdgeSettingsSection_module_css_default.mcpCardTitleGroup,
919
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
920
+ className: EdgeSettingsSection_module_css_default.mcpCardName,
921
+ children: s.serverName
922
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
923
+ className: EdgeSettingsSection_module_css_default.mcpCardSub,
924
+ children: s.auth?.type !== void 0 && s.auth.type !== "none" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
925
+ className: EdgeSettingsSection_module_css_default.mcpBadge,
926
+ "data-status": "unknown",
927
+ children: s.auth.type.toUpperCase()
928
+ }) : null
929
+ })]
930
+ }),
931
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronIcon, { expanded })
932
+ ]
933
+ }), expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
934
+ className: EdgeSettingsSection_module_css_default.mcpCardBody,
935
+ children: [
936
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
937
+ className: EdgeSettingsSection_module_css_default.mcpFieldRow,
938
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
939
+ className: EdgeSettingsSection_module_css_default.mcpFieldLabel,
940
+ children: "URL"
941
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
942
+ className: EdgeSettingsSection_module_css_default.mcpFieldValue,
943
+ style: {
944
+ fontSize: 12,
945
+ wordBreak: "break-all"
946
+ },
947
+ children: s.url
948
+ })]
949
+ }),
950
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
951
+ className: EdgeSettingsSection_module_css_default.mcpFieldRow,
952
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
953
+ className: EdgeSettingsSection_module_css_default.mcpFieldLabel,
954
+ children: t("mcpAuthLabel")
955
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
956
+ className: EdgeSettingsSection_module_css_default.mcpFieldValue,
957
+ children: authLabel
958
+ })]
959
+ }),
960
+ s.serverInfo?.name !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
961
+ className: EdgeSettingsSection_module_css_default.mcpFieldRow,
962
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
963
+ className: EdgeSettingsSection_module_css_default.mcpFieldLabel,
964
+ children: t("mcpServerInfo")
965
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
966
+ className: EdgeSettingsSection_module_css_default.mcpFieldValue,
967
+ children: [s.serverInfo.name, s.serverInfo.version !== void 0 ? ` v${s.serverInfo.version}` : ""]
968
+ })]
969
+ }) : null,
970
+ s.status === "connected" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
971
+ className: EdgeSettingsSection_module_css_default.mcpCardSuccess,
972
+ children: [
973
+ s.toolCount ?? 0,
974
+ " ",
975
+ t("mcpTools"),
976
+ " · ",
977
+ t("mcpConnected")
978
+ ]
979
+ }) : s.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
980
+ className: EdgeSettingsSection_module_css_default.mcpCardError,
981
+ children: t("mcpConnectionFailed")
982
+ }) : null,
983
+ s.status === "connected" || hasTools ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
984
+ className: EdgeSettingsSection_module_css_default.mcpFieldRow,
985
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
986
+ className: EdgeSettingsSection_module_css_default.mcpFieldLabel,
987
+ children: t("mcpToolPolicy")
988
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
989
+ className: EdgeSettingsSection_module_css_default.select,
990
+ value: s.toolPolicy?.mode ?? "read_only",
991
+ disabled: saving || disabled,
992
+ "aria-label": t("mcpToolPolicy"),
993
+ onChange: (e) => onSetToolPolicy(e.target.value),
994
+ children: [
995
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
996
+ value: "approve_all",
997
+ children: t("mcpPolicyApproveAll")
998
+ }),
999
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1000
+ value: "read_only",
1001
+ children: t("mcpPolicyReadOnly")
1002
+ }),
1003
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1004
+ value: "allow_all",
1005
+ children: t("mcpPolicyAllowAll")
1006
+ })
1007
+ ]
1008
+ })]
1009
+ }) : null,
1010
+ hasTools && tools !== null && tools.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1011
+ className: EdgeSettingsSection_module_css_default.mcpToolList,
1012
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1013
+ className: EdgeSettingsSection_module_css_default.mcpFieldLabel,
1014
+ children: [
1015
+ t("mcpToolsLabel"),
1016
+ " (",
1017
+ tools.length,
1018
+ ")"
1019
+ ]
1020
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
1021
+ className: EdgeSettingsSection_module_css_default.mcpToolItems,
1022
+ children: tools.map((tool) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
1023
+ className: EdgeSettingsSection_module_css_default.mcpToolItem,
1024
+ children: [
1025
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
1026
+ className: EdgeSettingsSection_module_css_default.mcpToolName,
1027
+ children: tool.name
1028
+ }),
1029
+ tool.readOnly === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1030
+ className: EdgeSettingsSection_module_css_default.mcpBadge,
1031
+ "data-status": "connected",
1032
+ children: t("mcpRead")
1033
+ }) : tool.readOnly === false ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1034
+ className: EdgeSettingsSection_module_css_default.mcpBadge,
1035
+ "data-status": "error",
1036
+ children: t("mcpWrite")
1037
+ }) : null,
1038
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1039
+ className: EdgeSettingsSection_module_css_default.mcpToolDesc,
1040
+ children: tool.description
1041
+ })
1042
+ ]
1043
+ }, tool.publicName))
1044
+ })]
1045
+ }) : toolsLoading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1046
+ className: EdgeSettingsSection_module_css_default.mcpFieldLabel,
1047
+ children: t("loading")
1048
+ }) : null,
1049
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1050
+ className: EdgeSettingsSection_module_css_default.mcpCardActions,
1051
+ children: [s.auth?.type === "oauth" && s.status !== "connected" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1052
+ variant: "outline",
1053
+ size: "sm",
1054
+ disabled: saving || disabled || connecting,
1055
+ onClick: onOAuthConnect,
1056
+ children: connecting ? t("mcpConnecting") : s.status === "needs_reauth" ? t("mcpReconnect") : t("mcpConnect")
1057
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1058
+ variant: "outline",
1059
+ size: "sm",
1060
+ disabled: saving || disabled || probing,
1061
+ onClick: () => {
1062
+ setProbing(true);
1063
+ onProbe().then(() => onGetTools().then(setTools)).finally(() => setProbing(false));
1064
+ },
1065
+ children: probing ? t("mcpProbing") : t("mcpRefresh")
1066
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1067
+ variant: "outline",
1068
+ size: "sm",
1069
+ disabled: saving || disabled,
1070
+ onClick: onRemove,
1071
+ children: t("mcpRemove")
1072
+ })]
1073
+ })
1074
+ ]
1075
+ }) : null]
1076
+ });
1077
+ }
1078
+ function EdgeMcpSection(props) {
1079
+ const { useEdgeSettings, load, saveMcpServers, saveMcpToken, setMcpToolPolicy, probeMcpServer, getMcpTools, startOAuthConnect, t } = props;
1080
+ (0, react.useEffect)(() => {
1081
+ load();
1082
+ }, [load]);
1083
+ const state = useEdgeSettings((snapshot) => snapshot);
1084
+ const servers = state.mcpServers;
1085
+ const saving = state.mcpSaving;
1086
+ const disabled = state.status !== "ready" || !state.mcpLoaded;
1087
+ const [expandedServer, setExpandedServer] = (0, react.useState)(null);
1088
+ const [connectingServer, setConnectingServer] = (0, react.useState)(null);
1089
+ const [popupError, setPopupError] = (0, react.useState)(null);
1090
+ const [showAddForm, setShowAddForm] = (0, react.useState)(false);
1091
+ (0, react.useEffect)(() => {
1092
+ const handler = (e) => {
1093
+ if (e.data?.type === "mcp-oauth-complete") setConnectingServer(null);
1094
+ };
1095
+ globalThis.addEventListener?.("message", handler);
1096
+ return () => globalThis.removeEventListener?.("message", handler);
1097
+ }, []);
1098
+ const [draft, setDraft] = (0, react.useState)({
1099
+ serverName: "",
1100
+ url: "",
1101
+ authType: "none",
1102
+ token: ""
1103
+ });
1104
+ const addServer = (0, react.useCallback)(() => {
1105
+ if (draft.serverName.trim() === "" || draft.url.trim() === "") return;
1106
+ const entry = {
1107
+ serverName: draft.serverName.trim(),
1108
+ url: draft.url.trim(),
1109
+ auth: { type: draft.authType }
1110
+ };
1111
+ (async () => {
1112
+ const name = draft.serverName.trim();
1113
+ if (!/^[A-Za-z0-9_-]{1,32}$/u.test(name)) return;
1114
+ try {
1115
+ new URL(draft.url.trim());
1116
+ } catch {
1117
+ return;
1118
+ }
1119
+ if (servers.some((s) => s.serverName === name)) return;
1120
+ if (draft.authType === "bearer" && draft.token.trim() !== "") {
1121
+ if (!await saveMcpToken(name, draft.token.trim())) return;
1122
+ }
1123
+ if (!await saveMcpServers([...servers, entry])) return;
1124
+ const addedName = name;
1125
+ const addedUrl = draft.url.trim();
1126
+ const addedAuth = draft.authType;
1127
+ setDraft({
1128
+ serverName: "",
1129
+ url: "",
1130
+ authType: "none",
1131
+ token: ""
1132
+ });
1133
+ setShowAddForm(false);
1134
+ setExpandedServer(addedName);
1135
+ if (addedAuth === "oauth") {
1136
+ setConnectingServer(addedName);
1137
+ const authUrl = await startOAuthConnect(addedName, addedUrl);
1138
+ if (authUrl === void 0) setConnectingServer(null);
1139
+ else {
1140
+ const popup = globalThis.open(authUrl, "_blank", "width=600,height=700");
1141
+ if (!popup) {
1142
+ setConnectingServer(null);
1143
+ setPopupError(addedName);
1144
+ } else {
1145
+ const check = setInterval(() => {
1146
+ if (popup.closed) {
1147
+ clearInterval(check);
1148
+ setConnectingServer((c) => c === addedName ? null : c);
1149
+ }
1150
+ }, 500);
1151
+ }
1152
+ }
1153
+ }
1154
+ })();
1155
+ }, [
1156
+ draft,
1157
+ servers,
1158
+ saveMcpServers,
1159
+ saveMcpToken
1160
+ ]);
1161
+ const removeServer = (0, react.useCallback)((name) => {
1162
+ saveMcpServers(servers.filter((s) => s.serverName !== name));
1163
+ if (expandedServer === name) setExpandedServer(null);
1164
+ }, [
1165
+ servers,
1166
+ saveMcpServers,
1167
+ expandedServer
1168
+ ]);
1169
+ if (state.status === "idle" || state.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1170
+ className: EdgeSettingsSection_module_css_default.section,
1171
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1172
+ className: EdgeSettingsSection_module_css_default.notice,
1173
+ children: t("loading")
1174
+ })
1175
+ });
1176
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1177
+ className: EdgeSettingsSection_module_css_default.section,
1178
+ children: [
1179
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: t("mcpServers") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1180
+ style: { margin: "4px 0 0" },
1181
+ children: t("mcpIntro")
1182
+ })] }),
1183
+ servers.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
1184
+ className: EdgeSettingsSection_module_css_default.mcpList,
1185
+ children: servers.map((s) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(McpConnectorCard, {
1186
+ server: s,
1187
+ saving,
1188
+ disabled,
1189
+ expanded: expandedServer === s.serverName,
1190
+ onToggle: () => setExpandedServer((prev) => prev === s.serverName ? null : s.serverName),
1191
+ onRemove: () => removeServer(s.serverName),
1192
+ onSetToolPolicy: (mode) => {
1193
+ setMcpToolPolicy(s.serverName, mode);
1194
+ },
1195
+ onProbe: () => probeMcpServer(s.serverName),
1196
+ onGetTools: () => getMcpTools(s.serverName),
1197
+ onOAuthConnect: () => {
1198
+ setConnectingServer(s.serverName);
1199
+ setPopupError(null);
1200
+ startOAuthConnect(s.serverName, s.url).then((authUrl) => {
1201
+ if (authUrl === void 0) {
1202
+ setConnectingServer(null);
1203
+ return;
1204
+ }
1205
+ const popup = globalThis.open(authUrl, "_blank", "width=600,height=700");
1206
+ if (!popup) {
1207
+ setConnectingServer(null);
1208
+ setPopupError(s.serverName);
1209
+ return;
1210
+ }
1211
+ const check = setInterval(() => {
1212
+ if (popup.closed) {
1213
+ clearInterval(check);
1214
+ setConnectingServer((c) => c === s.serverName ? null : c);
1215
+ }
1216
+ }, 500);
1217
+ });
1218
+ },
1219
+ connecting: connectingServer === s.serverName,
1220
+ t
1221
+ }) }, s.serverName))
1222
+ }) : null,
1223
+ !showAddForm ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1224
+ type: "button",
1225
+ className: EdgeSettingsSection_module_css_default.mcpAddTrigger,
1226
+ disabled: saving || disabled,
1227
+ onClick: () => setShowAddForm(true),
1228
+ children: ["+ ", t("mcpAddServer")]
1229
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1230
+ className: EdgeSettingsSection_module_css_default.mcpAddForm,
1231
+ children: [
1232
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1233
+ className: EdgeSettingsSection_module_css_default.mcpFormRow,
1234
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1235
+ className: EdgeSettingsSection_module_css_default.input,
1236
+ placeholder: t("mcpNamePlaceholder"),
1237
+ value: draft.serverName,
1238
+ disabled: saving || disabled,
1239
+ onChange: (e) => setDraft((d) => ({
1240
+ ...d,
1241
+ serverName: e.target.value
1242
+ }))
1243
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1244
+ className: EdgeSettingsSection_module_css_default.input,
1245
+ placeholder: t("mcpUrlPlaceholder"),
1246
+ value: draft.url,
1247
+ disabled: saving || disabled,
1248
+ onChange: (e) => setDraft((d) => ({
1249
+ ...d,
1250
+ url: e.target.value
1251
+ }))
1252
+ })]
1253
+ }),
1254
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1255
+ className: EdgeSettingsSection_module_css_default.mcpFormRow,
1256
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1257
+ className: EdgeSettingsSection_module_css_default.select,
1258
+ style: { width: "100%" },
1259
+ value: draft.authType,
1260
+ disabled: saving || disabled,
1261
+ "aria-label": "Auth type",
1262
+ onChange: (e) => setDraft((d) => ({
1263
+ ...d,
1264
+ authType: e.target.value
1265
+ })),
1266
+ children: [
1267
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1268
+ value: "none",
1269
+ children: t("mcpAuthNone")
1270
+ }),
1271
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1272
+ value: "bearer",
1273
+ children: t("mcpAuthBearer")
1274
+ }),
1275
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1276
+ value: "oauth",
1277
+ children: t("mcpAuthOAuth")
1278
+ })
1279
+ ]
1280
+ }), draft.authType === "bearer" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1281
+ className: EdgeSettingsSection_module_css_default.input,
1282
+ type: "password",
1283
+ placeholder: t("mcpTokenPlaceholder"),
1284
+ value: draft.token,
1285
+ disabled: saving || disabled,
1286
+ onChange: (e) => setDraft((d) => ({
1287
+ ...d,
1288
+ token: e.target.value
1289
+ }))
1290
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {})]
1291
+ }),
1292
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1293
+ className: EdgeSettingsSection_module_css_default.mcpFormActions,
1294
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1295
+ variant: "outline",
1296
+ size: "sm",
1297
+ onClick: () => {
1298
+ setShowAddForm(false);
1299
+ setDraft({
1300
+ serverName: "",
1301
+ url: "",
1302
+ authType: "none",
1303
+ token: ""
1304
+ });
1305
+ },
1306
+ children: t("mcpCancel")
1307
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1308
+ variant: "outline",
1309
+ size: "sm",
1310
+ disabled: saving || disabled || draft.serverName.trim() === "" || draft.url.trim() === "" || draft.authType === "bearer" && draft.token.trim() === "",
1311
+ onClick: addServer,
1312
+ children: t("mcpAdd")
1313
+ })]
1314
+ })
1315
+ ]
1316
+ }),
1317
+ popupError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1318
+ className: EdgeSettingsSection_module_css_default.error,
1319
+ role: "alert",
1320
+ children: t("mcpPopupBlocked")
1321
+ }) : null,
1322
+ state.mcpError !== void 0 && popupError === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1323
+ className: EdgeSettingsSection_module_css_default.error,
1324
+ role: "alert",
1325
+ children: state.mcpError
1326
+ }) : null
1327
+ ]
1328
+ });
1329
+ }
1330
+ //#endregion
526
1331
  //#region ../../../packages/client/ui-edge/src/client/locales.ts
527
1332
  /** English copy is the key-set source of truth for the Edge settings page. */
528
1333
  const en = {
@@ -556,7 +1361,48 @@ window.__ModuleLoader__.load({
556
1361
  ownerIntro: "Signing out clears this browser’s owner session. Your deployment and data remain unchanged.",
557
1362
  signOut: "Sign out",
558
1363
  signingOut: "Signing out…",
559
- signOutFailed: "Could not sign out. Try again."
1364
+ signOutFailed: "Could not sign out. Try again.",
1365
+ toolPermissions: "Tool permissions",
1366
+ approvalMode: "Web fetch tools",
1367
+ approvalAsk: "Always ask before running",
1368
+ approvalNever: "Trust all (no confirmation)",
1369
+ approvalSaved: "Saved",
1370
+ approvalError: "Could not save setting.",
1371
+ mcpServers: "MCP Connectors",
1372
+ mcpIntro: "Connect external tool servers to extend your agent with additional capabilities.",
1373
+ mcpEmpty: "No MCP servers configured.",
1374
+ mcpNamePlaceholder: "Server name (e.g. github)",
1375
+ mcpUrlPlaceholder: "Endpoint URL (e.g. https://mcp.example.com/sse)",
1376
+ mcpAdd: "Add server",
1377
+ mcpRemove: "Remove",
1378
+ mcpAuthNone: "No auth",
1379
+ mcpAuthBearer: "Bearer",
1380
+ mcpTokenPlaceholder: "Bearer token",
1381
+ mcpTokenSaved: "Token saved",
1382
+ mcpAuthOAuth: "OAuth",
1383
+ mcpConnect: "Connect",
1384
+ mcpReconnect: "Reconnect",
1385
+ mcpConnected: "connected",
1386
+ mcpNeedsReauth: "needs reconnect",
1387
+ mcpError: "connection error",
1388
+ mcpTools: "tools",
1389
+ mcpToolPolicy: "Tool permissions",
1390
+ mcpPolicyApproveAll: "Ask before every call",
1391
+ mcpPolicyReadOnly: "Allow reads, ask for writes",
1392
+ mcpPolicyAllowAll: "Trust all (no confirmation)",
1393
+ mcpNav: "MCP Connectors",
1394
+ mcpAddServer: "Add connector",
1395
+ mcpCancel: "Cancel",
1396
+ mcpAuthLabel: "Authentication",
1397
+ mcpServerInfo: "Server",
1398
+ mcpConnecting: "Connecting…",
1399
+ mcpPopupBlocked: "Pop-up blocked by browser. Please allow pop-ups for this site and try again.",
1400
+ mcpConnectionFailed: "Connection failed. Check the URL and authentication type.",
1401
+ mcpRefresh: "Refresh",
1402
+ mcpProbing: "Refreshing…",
1403
+ mcpToolsLabel: "Tools",
1404
+ mcpRead: "read",
1405
+ mcpWrite: "write"
560
1406
  };
561
1407
  /** Simplified Chinese copy for the Edge settings page. */
562
1408
  const zh = {
@@ -590,7 +1436,48 @@ window.__ModuleLoader__.load({
590
1436
  ownerIntro: "退出只会清除此浏览器的 Owner 会话,不会改变部署或数据。",
591
1437
  signOut: "退出登录",
592
1438
  signingOut: "正在退出…",
593
- signOutFailed: "退出失败,请重试。"
1439
+ signOutFailed: "退出失败,请重试。",
1440
+ toolPermissions: "工具权限",
1441
+ approvalMode: "Web fetch 工具",
1442
+ approvalAsk: "每次都询问",
1443
+ approvalNever: "全部信任(不询问)",
1444
+ approvalSaved: "已保存",
1445
+ approvalError: "无法保存设置。",
1446
+ mcpServers: "MCP 连接器",
1447
+ mcpIntro: "连接外部工具服务器,为 Agent 扩展更多能力。",
1448
+ mcpEmpty: "尚未配置 MCP 服务器。",
1449
+ mcpNamePlaceholder: "服务器名称(如 github)",
1450
+ mcpUrlPlaceholder: "端点 URL(如 https://mcp.example.com/sse)",
1451
+ mcpAdd: "添加服务器",
1452
+ mcpRemove: "移除",
1453
+ mcpAuthNone: "无认证",
1454
+ mcpAuthBearer: "Bearer Token",
1455
+ mcpTokenPlaceholder: "Bearer Token",
1456
+ mcpTokenSaved: "Token 已保存",
1457
+ mcpAuthOAuth: "OAuth",
1458
+ mcpConnect: "连接",
1459
+ mcpReconnect: "重新连接",
1460
+ mcpConnected: "已连接",
1461
+ mcpNeedsReauth: "需要重连",
1462
+ mcpError: "连接错误",
1463
+ mcpTools: "个工具",
1464
+ mcpToolPolicy: "工具权限",
1465
+ mcpPolicyApproveAll: "每次调用都询问",
1466
+ mcpPolicyReadOnly: "读取放行,写入询问",
1467
+ mcpPolicyAllowAll: "全部信任(不询问)",
1468
+ mcpNav: "MCP 连接器",
1469
+ mcpAddServer: "添加连接器",
1470
+ mcpCancel: "取消",
1471
+ mcpAuthLabel: "认证方式",
1472
+ mcpServerInfo: "服务器",
1473
+ mcpConnecting: "连接中…",
1474
+ mcpPopupBlocked: "弹窗被浏览器拦截,请允许此站点的弹窗后重试。",
1475
+ mcpConnectionFailed: "连接失败,请检查 URL 和认证方式是否正确。",
1476
+ mcpRefresh: "刷新",
1477
+ mcpProbing: "刷新中…",
1478
+ mcpToolsLabel: "工具",
1479
+ mcpRead: "读取",
1480
+ mcpWrite: "写入"
594
1481
  };
595
1482
  //#endregion
596
1483
  //#region ../../../packages/client/ui-edge/src/client/index.ts
@@ -619,11 +1506,22 @@ window.__ModuleLoader__.load({
619
1506
  globalThis.location.assign(path);
620
1507
  }
621
1508
  });
622
- const injected = () => ({
1509
+ const edgeInjected = () => ({
623
1510
  hooks: { edgeSettings: controller.store },
624
1511
  load: () => controller.load(),
625
1512
  copyUpgrade: () => controller.copyUpgrade(),
626
- signOut: () => controller.signOut()
1513
+ signOut: () => controller.signOut(),
1514
+ setApprovalMode: (mode) => controller.setApprovalMode(mode)
1515
+ });
1516
+ const mcpInjected = () => ({
1517
+ hooks: { edgeSettings: controller.store },
1518
+ load: () => controller.load(),
1519
+ saveMcpServers: (servers) => controller.saveMcpServers(servers),
1520
+ saveMcpToken: (name, token) => controller.saveMcpToken(name, token),
1521
+ setMcpToolPolicy: (name, mode) => controller.setMcpToolPolicy(name, mode),
1522
+ probeMcpServer: (name) => controller.probeMcpServer(name),
1523
+ getMcpTools: (name) => controller.getMcpTools(name),
1524
+ startOAuthConnect: (name, url) => controller.startOAuthConnect(name, url)
627
1525
  });
628
1526
  slots.inject("settings.section", () => slots.register({
629
1527
  name: "settings.section",
@@ -631,8 +1529,16 @@ window.__ModuleLoader__.load({
631
1529
  order: 90,
632
1530
  label: () => ctx.locale.bind("settings.edge")("nav"),
633
1531
  locale: "settings.edge",
634
- inject: injected
1532
+ inject: edgeInjected
635
1533
  }, EdgeSettingsSection));
1534
+ slots.inject("settings.section", () => slots.register({
1535
+ name: "settings.section",
1536
+ id: "mcp-connectors",
1537
+ order: 55,
1538
+ label: () => ctx.locale.bind("settings.edge")("mcpNav"),
1539
+ locale: "settings.edge",
1540
+ inject: mcpInjected
1541
+ }, EdgeMcpSection));
636
1542
  ctx.inject(["remote.session"], (scope) => {
637
1543
  scope.effect(() => {
638
1544
  const session = scope.get("remote.session");