mcp-squared 0.3.3 → 0.3.4

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/index.js CHANGED
@@ -1872,18 +1872,32 @@ function readManifestFile(manifestUrl) {
1872
1872
  }
1873
1873
  function readBundledManifestFile() {
1874
1874
  const require2 = createRequire(import.meta.url);
1875
- return require2("../package.json");
1875
+ const candidates = ["../package.json", "../../package.json"];
1876
+ for (const candidate of candidates) {
1877
+ try {
1878
+ return require2(candidate);
1879
+ } catch {}
1880
+ }
1881
+ throw new Error("Unable to resolve bundled package.json");
1882
+ }
1883
+ function getDefaultManifestUrls() {
1884
+ return [
1885
+ new URL("../package.json", import.meta.url),
1886
+ new URL("../../package.json", import.meta.url)
1887
+ ];
1876
1888
  }
1877
1889
  function resolveVersion(options = {}) {
1878
1890
  const readManifest = options.readManifest ?? readManifestFile;
1879
- const manifestUrl = options.manifestUrl ?? new URL("../package.json", import.meta.url);
1880
- try {
1881
- const manifest = readManifest(manifestUrl);
1882
- const manifestVersion = normalizeVersion(manifest.version);
1883
- if (manifestVersion) {
1884
- return manifestVersion;
1885
- }
1886
- } catch {}
1891
+ const manifestUrls = options.manifestUrls ? [...options.manifestUrls] : options.manifestUrl ? [options.manifestUrl] : getDefaultManifestUrls();
1892
+ for (const manifestUrl of manifestUrls) {
1893
+ try {
1894
+ const manifest = readManifest(manifestUrl);
1895
+ const manifestVersion = normalizeVersion(manifest.version);
1896
+ if (manifestVersion) {
1897
+ return manifestVersion;
1898
+ }
1899
+ } catch {}
1900
+ }
1887
1901
  const envVersion = normalizeVersion((options.env ?? process.env)["npm_package_version"]);
1888
1902
  if (envVersion) {
1889
1903
  return envVersion;
@@ -1006,18 +1006,32 @@ function readManifestFile(manifestUrl) {
1006
1006
  }
1007
1007
  function readBundledManifestFile() {
1008
1008
  const require2 = createRequire(import.meta.url);
1009
- return require2("../package.json");
1009
+ const candidates = ["../package.json", "../../package.json"];
1010
+ for (const candidate of candidates) {
1011
+ try {
1012
+ return require2(candidate);
1013
+ } catch {}
1014
+ }
1015
+ throw new Error("Unable to resolve bundled package.json");
1016
+ }
1017
+ function getDefaultManifestUrls() {
1018
+ return [
1019
+ new URL("../package.json", import.meta.url),
1020
+ new URL("../../package.json", import.meta.url)
1021
+ ];
1010
1022
  }
1011
1023
  function resolveVersion(options = {}) {
1012
1024
  const readManifest = options.readManifest ?? readManifestFile;
1013
- const manifestUrl = options.manifestUrl ?? new URL("../package.json", import.meta.url);
1014
- try {
1015
- const manifest = readManifest(manifestUrl);
1016
- const manifestVersion = normalizeVersion(manifest.version);
1017
- if (manifestVersion) {
1018
- return manifestVersion;
1019
- }
1020
- } catch {}
1025
+ const manifestUrls = options.manifestUrls ? [...options.manifestUrls] : options.manifestUrl ? [options.manifestUrl] : getDefaultManifestUrls();
1026
+ for (const manifestUrl of manifestUrls) {
1027
+ try {
1028
+ const manifest = readManifest(manifestUrl);
1029
+ const manifestVersion = normalizeVersion(manifest.version);
1030
+ if (manifestVersion) {
1031
+ return manifestVersion;
1032
+ }
1033
+ } catch {}
1034
+ }
1021
1035
  const envVersion = normalizeVersion((options.env ?? process.env)["npm_package_version"]);
1022
1036
  if (envVersion) {
1023
1037
  return envVersion;
@@ -2241,6 +2255,132 @@ async function testUpstreamConnection(name, config, options = {}) {
2241
2255
  log("Done");
2242
2256
  }
2243
2257
  }
2258
+ // src/tui/upstream-edit.ts
2259
+ function hasNameConflict(upstreams, nextName, existingName) {
2260
+ return Boolean(existingName && nextName !== existingName && typeof upstreams[nextName] !== "undefined");
2261
+ }
2262
+ function parseKeyValuePairsInput(input) {
2263
+ const result = {};
2264
+ if (!input.trim())
2265
+ return result;
2266
+ const pairs = input.split(",").map((s) => s.trim()).filter(Boolean);
2267
+ for (const pair of pairs) {
2268
+ const eqIndex = pair.indexOf("=");
2269
+ if (eqIndex > 0) {
2270
+ const key = pair.substring(0, eqIndex).trim();
2271
+ const value = pair.substring(eqIndex + 1).trim();
2272
+ if (key) {
2273
+ result[key] = value;
2274
+ }
2275
+ }
2276
+ }
2277
+ return result;
2278
+ }
2279
+ function stringifyKeyValuePairsInput(pairs) {
2280
+ const entries = Object.entries(pairs || {});
2281
+ if (entries.length === 0) {
2282
+ return "";
2283
+ }
2284
+ return entries.map(([key, value]) => `${key}=${value}`).join(", ");
2285
+ }
2286
+ function saveStdioUpstreamFromForm(input) {
2287
+ const trimmedName = input.name.trim();
2288
+ const trimmedCommand = input.commandLine.trim();
2289
+ if (!trimmedName) {
2290
+ return { ok: false, reason: "name_required" };
2291
+ }
2292
+ if (!trimmedCommand) {
2293
+ return { ok: false, reason: "command_required" };
2294
+ }
2295
+ if (hasNameConflict(input.upstreams, trimmedName, input.existingName)) {
2296
+ return { ok: false, reason: "name_conflict" };
2297
+ }
2298
+ const parts = trimmedCommand.split(/\s+/);
2299
+ const command = parts[0] || "";
2300
+ const args = parts.slice(1);
2301
+ const env = parseKeyValuePairsInput(input.envInput);
2302
+ if (input.existingName && input.existingName !== trimmedName) {
2303
+ delete input.upstreams[input.existingName];
2304
+ }
2305
+ input.upstreams[trimmedName] = {
2306
+ transport: "stdio",
2307
+ enabled: input.existingUpstream?.enabled ?? true,
2308
+ env,
2309
+ stdio: { command, args }
2310
+ };
2311
+ return { ok: true, savedName: trimmedName };
2312
+ }
2313
+ function saveSseUpstreamFromForm(input) {
2314
+ const trimmedName = input.name.trim();
2315
+ const trimmedUrl = input.url.trim();
2316
+ if (!trimmedName) {
2317
+ return { ok: false, reason: "name_required" };
2318
+ }
2319
+ if (!trimmedUrl) {
2320
+ return { ok: false, reason: "url_required" };
2321
+ }
2322
+ try {
2323
+ new URL(trimmedUrl);
2324
+ } catch {
2325
+ return { ok: false, reason: "invalid_url" };
2326
+ }
2327
+ if (hasNameConflict(input.upstreams, trimmedName, input.existingName)) {
2328
+ return { ok: false, reason: "name_conflict" };
2329
+ }
2330
+ const headers = parseKeyValuePairsInput(input.headersInput);
2331
+ const env = parseKeyValuePairsInput(input.envInput);
2332
+ if (input.existingName && input.existingName !== trimmedName) {
2333
+ delete input.upstreams[input.existingName];
2334
+ }
2335
+ input.upstreams[trimmedName] = {
2336
+ transport: "sse",
2337
+ enabled: input.existingUpstream?.enabled ?? true,
2338
+ env,
2339
+ sse: {
2340
+ url: trimmedUrl,
2341
+ headers,
2342
+ auth: input.authEnabled ? true : undefined
2343
+ }
2344
+ };
2345
+ return { ok: true, savedName: trimmedName };
2346
+ }
2347
+ function deleteUpstreamByName(upstreams, name) {
2348
+ if (typeof upstreams[name] === "undefined") {
2349
+ return false;
2350
+ }
2351
+ delete upstreams[name];
2352
+ return true;
2353
+ }
2354
+ function getUpstreamEditMenuOptions(upstream) {
2355
+ return [
2356
+ {
2357
+ name: "Edit Configuration",
2358
+ description: "Update connection details and environment",
2359
+ value: "edit"
2360
+ },
2361
+ {
2362
+ name: "Test Connection",
2363
+ description: "Connect and list available tools",
2364
+ value: "test"
2365
+ },
2366
+ {
2367
+ name: upstream.enabled ? "Disable" : "Enable",
2368
+ description: upstream.enabled ? "Stop using this upstream" : "Start using this upstream",
2369
+ value: "toggle"
2370
+ },
2371
+ {
2372
+ name: "Delete",
2373
+ description: "Remove this upstream configuration",
2374
+ value: "delete"
2375
+ },
2376
+ {
2377
+ name: "\u2190 Back",
2378
+ description: "",
2379
+ value: "back"
2380
+ }
2381
+ ];
2382
+ }
2383
+
2244
2384
  // src/tui/config.ts
2245
2385
  var PROJECT_DESCRIPTION = "Mercury Control Plane";
2246
2386
  async function runConfigTui() {
@@ -2572,12 +2712,13 @@ class ConfigTuiApp {
2572
2712
  menu.focus();
2573
2713
  this.addInstructions("\u2191\u2193 Navigate | Enter Select | Esc Back");
2574
2714
  }
2575
- showAddStdioScreen() {
2715
+ showAddStdioScreen(existingName, existingUpstream) {
2576
2716
  this.state.currentScreen = "add-stdio";
2577
2717
  this.clearScreen();
2578
2718
  this.addHeader();
2579
2719
  if (!this.container)
2580
2720
  return;
2721
+ const isEditMode = Boolean(existingName && existingUpstream);
2581
2722
  const formBox = new BoxRenderable(this.renderer, {
2582
2723
  id: "add-stdio-box",
2583
2724
  width: 60,
@@ -2585,7 +2726,7 @@ class ConfigTuiApp {
2585
2726
  border: true,
2586
2727
  borderStyle: "single",
2587
2728
  borderColor: "#475569",
2588
- title: "Add Stdio Upstream",
2729
+ title: isEditMode ? `Edit Stdio Upstream: ${existingName}` : "Add Stdio Upstream",
2589
2730
  titleAlignment: "center",
2590
2731
  backgroundColor: "#1e293b",
2591
2732
  flexDirection: "column",
@@ -2612,6 +2753,7 @@ class ConfigTuiApp {
2612
2753
  }
2613
2754
  });
2614
2755
  formBox.add(nameInput);
2756
+ nameInput.value = existingName ?? "";
2615
2757
  const commandLabel = new TextRenderable(this.renderer, {
2616
2758
  id: "command-label",
2617
2759
  content: "Command (with arguments):",
@@ -2631,6 +2773,7 @@ class ConfigTuiApp {
2631
2773
  }
2632
2774
  });
2633
2775
  formBox.add(commandInput);
2776
+ commandInput.value = existingUpstream ? [existingUpstream.stdio.command, ...existingUpstream.stdio.args].filter(Boolean).join(" ") : "";
2634
2777
  const envLabel = new TextRenderable(this.renderer, {
2635
2778
  id: "env-label",
2636
2779
  content: "Environment variables (optional, comma-separated):",
@@ -2651,6 +2794,7 @@ class ConfigTuiApp {
2651
2794
  }
2652
2795
  });
2653
2796
  formBox.add(envInput);
2797
+ envInput.value = stringifyKeyValuePairsInput(existingUpstream?.env);
2654
2798
  const submitOptions = [
2655
2799
  { name: "[ Save Upstream ]", description: "", value: "save" },
2656
2800
  { name: "[ Cancel ]", description: "", value: "cancel" }
@@ -2675,44 +2819,28 @@ class ConfigTuiApp {
2675
2819
  if (field)
2676
2820
  field.focus();
2677
2821
  };
2678
- const parseEnvVars = (input) => {
2679
- const env = {};
2680
- if (!input.trim())
2681
- return env;
2682
- const pairs = input.split(",").map((s) => s.trim()).filter(Boolean);
2683
- for (const pair of pairs) {
2684
- const eqIndex = pair.indexOf("=");
2685
- if (eqIndex > 0) {
2686
- const key = pair.substring(0, eqIndex).trim();
2687
- const value = pair.substring(eqIndex + 1).trim();
2688
- if (key) {
2689
- env[key] = value;
2690
- }
2691
- }
2692
- }
2693
- return env;
2694
- };
2695
2822
  const saveUpstream = () => {
2696
- const trimmedName = nameInput.value?.trim() || "";
2697
- const trimmedCommand = commandInput.value?.trim() || "";
2698
- if (!trimmedName) {
2699
- nameInput.focus();
2700
- return;
2701
- }
2702
- if (!trimmedCommand) {
2703
- commandInput.focus();
2704
- return;
2823
+ const result = saveStdioUpstreamFromForm({
2824
+ upstreams: this.state.config.upstreams,
2825
+ name: nameInput.value || "",
2826
+ commandLine: commandInput.value || "",
2827
+ envInput: envInput.value || "",
2828
+ existingName: isEditMode ? existingName : undefined,
2829
+ existingUpstream
2830
+ });
2831
+ if (!result.ok) {
2832
+ switch (result.reason) {
2833
+ case "name_required":
2834
+ case "name_conflict":
2835
+ nameInput.focus();
2836
+ return;
2837
+ case "command_required":
2838
+ commandInput.focus();
2839
+ return;
2840
+ default:
2841
+ return;
2842
+ }
2705
2843
  }
2706
- const envVars = parseEnvVars(envInput.value || "");
2707
- const parts = trimmedCommand.split(/\s+/);
2708
- const command = parts[0] || "";
2709
- const args = parts.slice(1);
2710
- this.state.config.upstreams[trimmedName] = {
2711
- transport: "stdio",
2712
- enabled: true,
2713
- env: envVars,
2714
- stdio: { command, args }
2715
- };
2716
2844
  this.state.isDirty = true;
2717
2845
  cleanup();
2718
2846
  this.showUpstreamsScreen();
@@ -2722,13 +2850,21 @@ class ConfigTuiApp {
2722
2850
  saveUpstream();
2723
2851
  } else {
2724
2852
  cleanup();
2725
- this.showAddUpstreamScreen();
2853
+ if (isEditMode && existingName) {
2854
+ this.showEditUpstreamScreen(existingName);
2855
+ } else {
2856
+ this.showAddUpstreamScreen();
2857
+ }
2726
2858
  }
2727
2859
  });
2728
2860
  const handleKeypress = (key) => {
2729
2861
  if (key.name === "escape") {
2730
2862
  cleanup();
2731
- this.showAddUpstreamScreen();
2863
+ if (isEditMode && existingName) {
2864
+ this.showEditUpstreamScreen(existingName);
2865
+ } else {
2866
+ this.showAddUpstreamScreen();
2867
+ }
2732
2868
  return;
2733
2869
  }
2734
2870
  if (key.name === "tab" && !key.shift) {
@@ -2745,14 +2881,15 @@ class ConfigTuiApp {
2745
2881
  };
2746
2882
  this.renderer.keyInput.on("keypress", handleKeypress);
2747
2883
  focusField(0);
2748
- this.addInstructions("Tab: next field | Shift+Tab: prev | Esc: cancel");
2884
+ this.addInstructions("Tab: next field | Shift+Tab: prev | Esc: back");
2749
2885
  }
2750
- showAddSseScreen() {
2886
+ showAddSseScreen(existingName, existingUpstream) {
2751
2887
  this.state.currentScreen = "add-sse";
2752
2888
  this.clearScreen();
2753
2889
  this.addHeader();
2754
2890
  if (!this.container)
2755
2891
  return;
2892
+ const isEditMode = Boolean(existingName && existingUpstream);
2756
2893
  const formBox = new BoxRenderable(this.renderer, {
2757
2894
  id: "add-sse-box",
2758
2895
  width: 60,
@@ -2760,7 +2897,7 @@ class ConfigTuiApp {
2760
2897
  border: true,
2761
2898
  borderStyle: "single",
2762
2899
  borderColor: "#475569",
2763
- title: "Add HTTP/SSE Upstream",
2900
+ title: isEditMode ? `Edit HTTP/SSE Upstream: ${existingName}` : "Add HTTP/SSE Upstream",
2764
2901
  titleAlignment: "center",
2765
2902
  backgroundColor: "#1e293b",
2766
2903
  flexDirection: "column",
@@ -2787,6 +2924,7 @@ class ConfigTuiApp {
2787
2924
  }
2788
2925
  });
2789
2926
  formBox.add(nameInput);
2927
+ nameInput.value = existingName ?? "";
2790
2928
  const urlLabel = new TextRenderable(this.renderer, {
2791
2929
  id: "url-label",
2792
2930
  content: "Server URL:",
@@ -2806,6 +2944,7 @@ class ConfigTuiApp {
2806
2944
  }
2807
2945
  });
2808
2946
  formBox.add(urlInput);
2947
+ urlInput.value = existingUpstream?.sse.url ?? "";
2809
2948
  const headersLabel = new TextRenderable(this.renderer, {
2810
2949
  id: "headers-label",
2811
2950
  content: "HTTP Headers (optional, comma-separated):",
@@ -2826,6 +2965,7 @@ class ConfigTuiApp {
2826
2965
  }
2827
2966
  });
2828
2967
  formBox.add(headersInput);
2968
+ headersInput.value = stringifyKeyValuePairsInput(existingUpstream?.sse.headers);
2829
2969
  const authLabel = new TextRenderable(this.renderer, {
2830
2970
  id: "auth-label",
2831
2971
  content: "OAuth Authentication:",
@@ -2833,7 +2973,14 @@ class ConfigTuiApp {
2833
2973
  marginBottom: 0
2834
2974
  });
2835
2975
  formBox.add(authLabel);
2836
- const authOptions = [
2976
+ const authOptions = existingUpstream?.sse.auth ? [
2977
+ {
2978
+ name: "Enabled (default port 8089)",
2979
+ description: "",
2980
+ value: "enabled"
2981
+ },
2982
+ { name: "Disabled", description: "", value: "disabled" }
2983
+ ] : [
2837
2984
  { name: "Disabled", description: "", value: "disabled" },
2838
2985
  {
2839
2986
  name: "Enabled (default port 8089)",
@@ -2874,6 +3021,7 @@ class ConfigTuiApp {
2874
3021
  }
2875
3022
  });
2876
3023
  formBox.add(envInput);
3024
+ envInput.value = stringifyKeyValuePairsInput(existingUpstream?.env);
2877
3025
  const submitOptions = [
2878
3026
  { name: "[ Save Upstream ]", description: "", value: "save" },
2879
3027
  { name: "[ Cancel ]", description: "", value: "cancel" }
@@ -2899,63 +3047,41 @@ class ConfigTuiApp {
2899
3047
  submitSelect
2900
3048
  ];
2901
3049
  let focusIndex = 0;
2902
- let selectedAuthIndex = 0;
3050
+ let selectedAuthValue = String(authOptions[0]?.value ?? "disabled");
2903
3051
  const focusField = (index) => {
2904
3052
  focusIndex = index;
2905
3053
  const field = fields[index];
2906
3054
  if (field)
2907
3055
  field.focus();
2908
3056
  };
2909
- authSelect.on(SelectRenderableEvents.ITEM_SELECTED, (index, _opt) => {
2910
- selectedAuthIndex = index;
2911
- });
2912
- const parseKeyValuePairs = (input) => {
2913
- const result = {};
2914
- if (!input.trim())
2915
- return result;
2916
- const pairs = input.split(",").map((s) => s.trim()).filter(Boolean);
2917
- for (const pair of pairs) {
2918
- const eqIndex = pair.indexOf("=");
2919
- if (eqIndex > 0) {
2920
- const key = pair.substring(0, eqIndex).trim();
2921
- const value = pair.substring(eqIndex + 1).trim();
2922
- if (key) {
2923
- result[key] = value;
2924
- }
2925
- }
2926
- }
2927
- return result;
2928
- };
3057
+ authSelect.on(SelectRenderableEvents.ITEM_SELECTED, (_index, opt) => {
3058
+ selectedAuthValue = String(opt.value);
3059
+ });
2929
3060
  const saveUpstream = () => {
2930
- const trimmedName = nameInput.value?.trim() || "";
2931
- const trimmedUrl = urlInput.value?.trim() || "";
2932
- if (!trimmedName) {
2933
- nameInput.focus();
2934
- return;
2935
- }
2936
- if (!trimmedUrl) {
2937
- urlInput.focus();
2938
- return;
2939
- }
2940
- try {
2941
- new URL(trimmedUrl);
2942
- } catch {
2943
- urlInput.focus();
2944
- return;
2945
- }
2946
- const envVars = parseKeyValuePairs(envInput.value || "");
2947
- const headers = parseKeyValuePairs(headersInput.value || "");
2948
- const authEnabled = selectedAuthIndex === 1;
2949
- this.state.config.upstreams[trimmedName] = {
2950
- transport: "sse",
2951
- enabled: true,
2952
- env: envVars,
2953
- sse: {
2954
- url: trimmedUrl,
2955
- headers,
2956
- auth: authEnabled ? true : undefined
3061
+ const result = saveSseUpstreamFromForm({
3062
+ upstreams: this.state.config.upstreams,
3063
+ name: nameInput.value || "",
3064
+ url: urlInput.value || "",
3065
+ headersInput: headersInput.value || "",
3066
+ envInput: envInput.value || "",
3067
+ authEnabled: selectedAuthValue === "enabled",
3068
+ existingName: isEditMode ? existingName : undefined,
3069
+ existingUpstream
3070
+ });
3071
+ if (!result.ok) {
3072
+ switch (result.reason) {
3073
+ case "name_required":
3074
+ case "name_conflict":
3075
+ nameInput.focus();
3076
+ return;
3077
+ case "url_required":
3078
+ case "invalid_url":
3079
+ urlInput.focus();
3080
+ return;
3081
+ default:
3082
+ return;
2957
3083
  }
2958
- };
3084
+ }
2959
3085
  this.state.isDirty = true;
2960
3086
  cleanup();
2961
3087
  this.showUpstreamsScreen();
@@ -2965,13 +3091,21 @@ class ConfigTuiApp {
2965
3091
  saveUpstream();
2966
3092
  } else {
2967
3093
  cleanup();
2968
- this.showAddUpstreamScreen();
3094
+ if (isEditMode && existingName) {
3095
+ this.showEditUpstreamScreen(existingName);
3096
+ } else {
3097
+ this.showAddUpstreamScreen();
3098
+ }
2969
3099
  }
2970
3100
  });
2971
3101
  const handleKeypress = (key) => {
2972
3102
  if (key.name === "escape") {
2973
3103
  cleanup();
2974
- this.showAddUpstreamScreen();
3104
+ if (isEditMode && existingName) {
3105
+ this.showEditUpstreamScreen(existingName);
3106
+ } else {
3107
+ this.showAddUpstreamScreen();
3108
+ }
2975
3109
  return;
2976
3110
  }
2977
3111
  if (key.name === "tab" && !key.shift) {
@@ -2988,7 +3122,7 @@ class ConfigTuiApp {
2988
3122
  };
2989
3123
  this.renderer.keyInput.on("keypress", handleKeypress);
2990
3124
  focusField(0);
2991
- this.addInstructions("Tab: next field | Shift+Tab: prev | Esc: cancel");
3125
+ this.addInstructions("Tab: next field | Shift+Tab: prev | Esc: back");
2992
3126
  }
2993
3127
  showEditUpstreamScreen(name) {
2994
3128
  this.state.currentScreen = "edit-upstream";
@@ -3091,28 +3225,7 @@ class ConfigTuiApp {
3091
3225
  });
3092
3226
  menuBox.add(noEnvText);
3093
3227
  }
3094
- const options = [
3095
- {
3096
- name: "Test Connection",
3097
- description: "Connect and list available tools",
3098
- value: "test"
3099
- },
3100
- {
3101
- name: upstream.enabled ? "Disable" : "Enable",
3102
- description: upstream.enabled ? "Stop using this upstream" : "Start using this upstream",
3103
- value: "toggle"
3104
- },
3105
- {
3106
- name: "Delete",
3107
- description: "Remove this upstream configuration",
3108
- value: "delete"
3109
- },
3110
- {
3111
- name: "\u2190 Back",
3112
- description: "",
3113
- value: "back"
3114
- }
3115
- ];
3228
+ const options = getUpstreamEditMenuOptions(upstream);
3116
3229
  const menu = new SelectRenderable(this.renderer, {
3117
3230
  id: "edit-menu",
3118
3231
  width: "100%",
@@ -3129,6 +3242,13 @@ class ConfigTuiApp {
3129
3242
  menuBox.add(menu);
3130
3243
  menu.on(SelectRenderableEvents.ITEM_SELECTED, (_index, option) => {
3131
3244
  switch (option.value) {
3245
+ case "edit":
3246
+ if (upstream.transport === "stdio") {
3247
+ this.showAddStdioScreen(name, upstream);
3248
+ } else {
3249
+ this.showAddSseScreen(name, upstream);
3250
+ }
3251
+ break;
3132
3252
  case "test":
3133
3253
  this.showTestScreen(name, upstream);
3134
3254
  break;
@@ -3138,8 +3258,7 @@ class ConfigTuiApp {
3138
3258
  this.showEditUpstreamScreen(name);
3139
3259
  break;
3140
3260
  case "delete":
3141
- delete this.state.config.upstreams[name];
3142
- this.state.isDirty = true;
3261
+ this.state.isDirty = deleteUpstreamByName(this.state.config.upstreams, name) || this.state.isDirty;
3143
3262
  this.showUpstreamsScreen();
3144
3263
  break;
3145
3264
  case "back":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-squared",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "MCP² (Mercury Control Plane) - A local-first meta-server and proxy for the Model Context Protocol",
5
5
  "author": "aditzel",
6
6
  "license": "Apache-2.0",