multicorn-shield 1.3.4 → 1.3.6

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/CHANGELOG.md CHANGED
@@ -9,6 +9,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  - Bump `version` in `package.json` before publishing to npm.
11
11
 
12
+ ## [1.3.6] - 2026-05-08
13
+
14
+ ### Fixed
15
+
16
+ - Hosted proxy URLs now embed the API key as a query parameter fallback for MCP clients that don't send static Authorization headers (fixes Cursor, Claude Desktop, and other clients that ignore the headers config during discovery)
17
+
18
+ ## [1.3.5] - 2026-05-08
19
+
20
+ ### Fixed
21
+
22
+ - Replace flow no longer shows duplicate agent names (deduplication fix from 1.3.4 was incomplete)
23
+
12
24
  ## [1.3.3] - 2026-05-08
13
25
 
14
26
  ### Fixed
@@ -1564,6 +1564,41 @@ async function createProxyConfig(baseUrl, apiKey, agentName, targetUrl, serverNa
1564
1564
  const data = envelope["data"];
1565
1565
  return typeof data?.["proxy_url"] === "string" ? data["proxy_url"] : "";
1566
1566
  }
1567
+ function shouldEmbedKeyInHostedProxyUrl(platform) {
1568
+ return HOSTED_PROXY_PLATFORMS_WITH_URL_KEY.has(platform);
1569
+ }
1570
+ function hostedProxyUrlWithKeyParam(proxyUrl, apiKey) {
1571
+ if (apiKey.length === 0) {
1572
+ process.stderr.write(
1573
+ style.yellow("\u26A0") + " Could not add key to proxy URL: API key is empty; using URL without key query parameter.\n"
1574
+ );
1575
+ return proxyUrl;
1576
+ }
1577
+ try {
1578
+ const u = new URL(proxyUrl);
1579
+ u.searchParams.set("key", apiKey);
1580
+ return u.toString();
1581
+ } catch (err) {
1582
+ const detail = err instanceof Error ? err.message : String(err);
1583
+ process.stderr.write(
1584
+ style.yellow("\u26A0") + " Could not parse proxy URL to append key query parameter; using URL unchanged. " + style.dim(detail) + "\n"
1585
+ );
1586
+ return proxyUrl;
1587
+ }
1588
+ }
1589
+ function formatHostedProxyUrlForStderr(platform, proxyUrl, apiKey) {
1590
+ if (!shouldEmbedKeyInHostedProxyUrl(platform) || apiKey.length === 0) {
1591
+ return proxyUrl;
1592
+ }
1593
+ try {
1594
+ const u = new URL(proxyUrl);
1595
+ const redactedLabel = apiKey.length <= 4 ? "****" : `mcs_...${apiKey.slice(-4)}`;
1596
+ u.searchParams.set("key", redactedLabel);
1597
+ return u.toString();
1598
+ } catch {
1599
+ return proxyUrl;
1600
+ }
1601
+ }
1567
1602
  function writeMcpAddedLine(shortName, filePath) {
1568
1603
  process.stderr.write(
1569
1604
  style.green("\u2713") + ' MCP server "' + shortName + '" added to ' + style.cyan(filePath) + "\n"
@@ -1713,8 +1748,9 @@ function printHostedProxyPostWriteHints(platform, shortName) {
1713
1748
  }
1714
1749
  async function applyHostedProxyMcpConfig(platform, proxyUrl, shortName, apiKey, workspacePath) {
1715
1750
  const authHeader = `Bearer ${apiKey}`;
1751
+ const proxyUrlWithKeyWhenNeeded = shouldEmbedKeyInHostedProxyUrl(platform) ? hostedProxyUrlWithKeyParam(proxyUrl, apiKey) : proxyUrl;
1716
1752
  if (platform === "gemini-cli") {
1717
- await mergeGeminiHostedMcpServersIntoSettings(shortName, proxyUrl, apiKey);
1753
+ await mergeGeminiHostedMcpServersIntoSettings(shortName, proxyUrlWithKeyWhenNeeded, apiKey);
1718
1754
  process.stderr.write(
1719
1755
  style.dim(
1720
1756
  "For project-specific config, copy the mcpServers entry into .gemini/settings.json in your project root. Restart Gemini CLI if it is already running."
@@ -1739,20 +1775,24 @@ async function applyHostedProxyMcpConfig(platform, proxyUrl, shortName, apiKey,
1739
1775
  let result = "parse-error";
1740
1776
  if (platform === "cursor") {
1741
1777
  result = await mergeMcpServersObjectStyle(getCursorMcpJsonPath(), shortName, {
1742
- url: proxyUrl,
1778
+ url: proxyUrlWithKeyWhenNeeded,
1743
1779
  headers: { Authorization: authHeader }
1744
1780
  });
1745
1781
  if (result === "parse-error") {
1746
1782
  printHostedProxyJsonParseWarning(getCursorMcpJsonPath());
1747
1783
  }
1748
1784
  } else if (platform === "claude-desktop") {
1749
- result = await mergeClaudeDesktopHostedMcpRemote(shortName, proxyUrl, apiKey);
1785
+ result = await mergeClaudeDesktopHostedMcpRemote(
1786
+ shortName,
1787
+ proxyUrlWithKeyWhenNeeded,
1788
+ apiKey
1789
+ );
1750
1790
  if (result === "parse-error") {
1751
1791
  printHostedProxyJsonParseWarning(getClaudeDesktopConfigPath());
1752
1792
  }
1753
1793
  } else if (platform === "windsurf") {
1754
1794
  result = await mergeMcpServersObjectStyle(getWindsurfMcpConfigPath(), shortName, {
1755
- serverUrl: proxyUrl,
1795
+ serverUrl: proxyUrlWithKeyWhenNeeded,
1756
1796
  headers: { Authorization: authHeader }
1757
1797
  });
1758
1798
  if (result === "parse-error") {
@@ -1760,25 +1800,30 @@ async function applyHostedProxyMcpConfig(platform, proxyUrl, shortName, apiKey,
1760
1800
  }
1761
1801
  } else if (platform === "cline") {
1762
1802
  result = await mergeMcpServersObjectStyle(getClineMcpSettingsPath(), shortName, {
1763
- url: proxyUrl,
1803
+ url: proxyUrlWithKeyWhenNeeded,
1764
1804
  headers: { Authorization: authHeader }
1765
1805
  });
1766
1806
  if (result === "parse-error") {
1767
1807
  printHostedProxyJsonParseWarning(getClineMcpSettingsPath());
1768
1808
  }
1769
1809
  } else if (platform === "kilo-code") {
1770
- result = await mergeKiloCodeProjectMcp(workspacePath, shortName, proxyUrl, apiKey);
1810
+ result = await mergeKiloCodeProjectMcp(
1811
+ workspacePath,
1812
+ shortName,
1813
+ proxyUrlWithKeyWhenNeeded,
1814
+ apiKey
1815
+ );
1771
1816
  if (result === "parse-error") {
1772
1817
  printHostedProxyJsonParseWarning(join(workspacePath, ".kilocode", "mcp.json"));
1773
1818
  }
1774
1819
  } else if (platform === "continue-dev") {
1775
- result = await mergeContinueHostedMcp(shortName, proxyUrl, apiKey);
1820
+ result = await mergeContinueHostedMcp(shortName, proxyUrlWithKeyWhenNeeded, apiKey);
1776
1821
  if (result === "parse-error") {
1777
1822
  printHostedProxyJsonParseWarning(getContinueConfigJsonPath());
1778
1823
  }
1779
1824
  } else {
1780
1825
  result = await mergeMcpServersObjectStyle(getCursorMcpJsonPath(), shortName, {
1781
- url: proxyUrl,
1826
+ url: proxyUrlWithKeyWhenNeeded,
1782
1827
  headers: { Authorization: authHeader }
1783
1828
  });
1784
1829
  if (result === "parse-error") {
@@ -1823,6 +1868,7 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
1823
1868
  ]);
1824
1869
  const usesInlineKey = hostedInlinePlatforms.has(platform);
1825
1870
  const authHeader = usesInlineKey ? `Bearer ${apiKey}` : "Bearer YOUR_SHIELD_API_KEY";
1871
+ const urlInSnippet = usesInlineKey && shouldEmbedKeyInHostedProxyUrl(platform) ? hostedProxyUrlWithKeyParam(routingToken, apiKey) : routingToken;
1826
1872
  let snippetText;
1827
1873
  if (platform === "github-copilot") {
1828
1874
  snippetText = JSON.stringify(
@@ -1831,7 +1877,7 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
1831
1877
  servers: {
1832
1878
  [shortName]: {
1833
1879
  type: "http",
1834
- url: routingToken,
1880
+ url: urlInSnippet,
1835
1881
  headers: {
1836
1882
  Authorization: authHeader
1837
1883
  }
@@ -1843,13 +1889,13 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
1843
1889
  2
1844
1890
  );
1845
1891
  } else if (platform === "goose") {
1846
- snippetText = gooseHostedProxyYaml(shortName, routingToken, authHeader);
1892
+ snippetText = gooseHostedProxyYaml(shortName, urlInSnippet, authHeader);
1847
1893
  } else if (platform === "gemini-cli") {
1848
1894
  snippetText = JSON.stringify(
1849
1895
  {
1850
1896
  mcpServers: {
1851
1897
  [shortName]: {
1852
- httpUrl: routingToken,
1898
+ httpUrl: urlInSnippet,
1853
1899
  headers: {
1854
1900
  Authorization: authHeader
1855
1901
  }
@@ -1865,7 +1911,7 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
1865
1911
  mcpServers: {
1866
1912
  [shortName]: {
1867
1913
  command: "npx",
1868
- args: ["-y", "mcp-remote", routingToken, "--header", `Authorization: ${authHeader}`]
1914
+ args: ["-y", "mcp-remote", urlInSnippet, "--header", `Authorization: ${authHeader}`]
1869
1915
  }
1870
1916
  }
1871
1917
  },
@@ -1879,7 +1925,7 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
1879
1925
  {
1880
1926
  name: shortName,
1881
1927
  type: "streamable-http",
1882
- url: routingToken,
1928
+ url: urlInSnippet,
1883
1929
  headers: {
1884
1930
  Authorization: authHeader
1885
1931
  }
@@ -1895,7 +1941,7 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
1895
1941
  {
1896
1942
  mcpServers: {
1897
1943
  [shortName]: {
1898
- [urlKey]: routingToken,
1944
+ [urlKey]: urlInSnippet,
1899
1945
  headers: {
1900
1946
  Authorization: authHeader
1901
1947
  }
@@ -1988,31 +2034,41 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
1988
2034
  process.stderr.write(style.dim("Start a new Goose session after updating config.") + "\n");
1989
2035
  }
1990
2036
  }
1991
- function dedupeAgentsByName(agents) {
1992
- const seen = /* @__PURE__ */ new Set();
1993
- const out = [];
1994
- for (const a of agents) {
1995
- if (seen.has(a.name)) continue;
1996
- seen.add(a.name);
1997
- out.push(a);
2037
+ function agentDisplayNameDedupeKey(name) {
2038
+ return name.trim().toLowerCase();
2039
+ }
2040
+ function normalizeAgentEntryForMerge(a) {
2041
+ const name = a.name.trim();
2042
+ const ws = typeof a.workspacePath === "string" && a.workspacePath.length > 0 ? a.workspacePath : void 0;
2043
+ return ws !== void 0 ? { name, platform: a.platform, workspacePath: ws } : { name, platform: a.platform };
2044
+ }
2045
+ function mergeAgentEntryDupPair(first, second) {
2046
+ const name = first.name.trim();
2047
+ const platform = first.platform;
2048
+ const ws = typeof first.workspacePath === "string" && first.workspacePath.length > 0 ? first.workspacePath : typeof second.workspacePath === "string" && second.workspacePath.length > 0 ? second.workspacePath : void 0;
2049
+ return ws !== void 0 ? { name, platform, workspacePath: ws } : { name, platform };
2050
+ }
2051
+ function mergeAgentsForUniqueNames(agents) {
2052
+ const byKey = /* @__PURE__ */ new Map();
2053
+ for (const raw of agents) {
2054
+ const key = agentDisplayNameDedupeKey(raw.name);
2055
+ const candidate = normalizeAgentEntryForMerge(raw);
2056
+ const prev = byKey.get(key);
2057
+ byKey.set(key, prev === void 0 ? candidate : mergeAgentEntryDupPair(prev, candidate));
1998
2058
  }
1999
- return out;
2059
+ return [...byKey.values()];
2000
2060
  }
2001
2061
  function mergeAgentsForPlatform(localAgents, remoteAgents, selectedPlatform) {
2002
- const byName = /* @__PURE__ */ new Map();
2062
+ const merged = [];
2003
2063
  for (const a of localAgents) {
2004
2064
  if (a.platform !== selectedPlatform) continue;
2005
- if (!byName.has(a.name)) {
2006
- byName.set(a.name, { ...a });
2007
- }
2065
+ merged.push(a);
2008
2066
  }
2009
2067
  for (const r of remoteAgents) {
2010
2068
  if (r.platform !== selectedPlatform) continue;
2011
- if (!byName.has(r.name)) {
2012
- byName.set(r.name, { name: r.name, platform: selectedPlatform });
2013
- }
2069
+ merged.push({ name: r.name, platform: selectedPlatform });
2014
2070
  }
2015
- return [...byName.values()];
2071
+ return mergeAgentsForUniqueNames(merged);
2016
2072
  }
2017
2073
  async function runInit(explicitBaseUrl, options) {
2018
2074
  const verbose = options?.verbose === true;
@@ -2151,8 +2207,10 @@ async function runInit(explicitBaseUrl, options) {
2151
2207
  continue;
2152
2208
  }
2153
2209
  const remoteAccountAgents = await fetchRemoteAgentsSummaries(apiKey, resolvedBaseUrl);
2154
- const agentsForPlatform = dedupeAgentsByName(
2155
- mergeAgentsForPlatform(currentAgents, remoteAccountAgents, selectedPlatform)
2210
+ const agentsForPlatform = mergeAgentsForPlatform(
2211
+ currentAgents,
2212
+ remoteAccountAgents,
2213
+ selectedPlatform
2156
2214
  );
2157
2215
  const localForPlatformCount = currentAgents.filter(
2158
2216
  (a) => a.platform === selectedPlatform
@@ -2414,7 +2472,9 @@ You have ${String(agentsForPlatform.length)} agent(s) connected for ${selectedLa
2414
2472
  }
2415
2473
  if (created && proxyUrl.length > 0) {
2416
2474
  process.stderr.write("\n" + style.bold("Your Shield proxy URL:") + "\n");
2417
- process.stderr.write(" " + style.cyan(proxyUrl) + "\n");
2475
+ process.stderr.write(
2476
+ " " + style.cyan(formatHostedProxyUrlForStderr(selectedPlatform, proxyUrl, apiKey)) + "\n"
2477
+ );
2418
2478
  await applyHostedProxyMcpConfig(
2419
2479
  selectedPlatform,
2420
2480
  proxyUrl,
@@ -2505,7 +2565,9 @@ You have ${String(agentsForPlatform.length)} agent(s) connected for ${selectedLa
2505
2565
  }
2506
2566
  if (created && proxyUrl.length > 0) {
2507
2567
  process.stderr.write("\n" + style.bold("Your Shield proxy URL:") + "\n");
2508
- process.stderr.write(" " + style.cyan(proxyUrl) + "\n");
2568
+ process.stderr.write(
2569
+ " " + style.cyan(formatHostedProxyUrlForStderr(selectedPlatform, proxyUrl, apiKey)) + "\n"
2570
+ );
2509
2571
  await applyHostedProxyMcpConfig(
2510
2572
  selectedPlatform,
2511
2573
  proxyUrl,
@@ -2590,7 +2652,9 @@ You have ${String(agentsForPlatform.length)} agent(s) connected for ${selectedLa
2590
2652
  }
2591
2653
  if (created && proxyUrl.length > 0) {
2592
2654
  process.stderr.write("\n" + style.bold("Your Shield proxy URL:") + "\n");
2593
- process.stderr.write(" " + style.cyan(proxyUrl) + "\n");
2655
+ process.stderr.write(
2656
+ " " + style.cyan(formatHostedProxyUrlForStderr(selectedPlatform, proxyUrl, apiKey)) + "\n"
2657
+ );
2594
2658
  await applyHostedProxyMcpConfig(
2595
2659
  selectedPlatform,
2596
2660
  proxyUrl,
@@ -2638,7 +2702,9 @@ You have ${String(agentsForPlatform.length)} agent(s) connected for ${selectedLa
2638
2702
  }
2639
2703
  if (created && proxyUrl.length > 0) {
2640
2704
  process.stderr.write("\n" + style.bold("Your Shield proxy URL:") + "\n");
2641
- process.stderr.write(" " + style.cyan(proxyUrl) + "\n");
2705
+ process.stderr.write(
2706
+ " " + style.cyan(formatHostedProxyUrlForStderr(selectedPlatform, proxyUrl, apiKey)) + "\n"
2707
+ );
2642
2708
  await applyHostedProxyMcpConfig(
2643
2709
  selectedPlatform,
2644
2710
  proxyUrl,
@@ -2659,7 +2725,10 @@ You have ${String(agentsForPlatform.length)} agent(s) connected for ${selectedLa
2659
2725
  }
2660
2726
  if (setupSucceeded) {
2661
2727
  if (removeAgentNameBeforeSave !== void 0) {
2662
- currentAgents = currentAgents.filter((a) => a.name !== removeAgentNameBeforeSave);
2728
+ const removeKey = agentDisplayNameDedupeKey(removeAgentNameBeforeSave);
2729
+ currentAgents = currentAgents.filter(
2730
+ (a) => agentDisplayNameDedupeKey(a.name) !== removeKey
2731
+ );
2663
2732
  }
2664
2733
  currentAgents.push({
2665
2734
  name: agentName,
@@ -2816,7 +2885,7 @@ You have ${String(agentsForPlatform.length)} agent(s) connected for ${selectedLa
2816
2885
  }
2817
2886
  return lastConfig;
2818
2887
  }
2819
- var SECRET_JSON_FILE_OPTIONS, style, BANNER, NativePluginPrerequisiteMissingError, CONFIG_DIR, CONFIG_PATH, OPENCLAW_CONFIG_PATH, ANSI_PATTERN, OPENCLAW_MIN_VERSION, INIT_WIZARD_PLATFORM_REGISTRY, INIT_WIZARD_MENU_SECTIONS, INIT_WIZARD_SELECTION_MAX, PLATFORM_BY_SELECTION, DEFAULT_SHIELD_API_BASE_URL;
2888
+ var SECRET_JSON_FILE_OPTIONS, style, BANNER, NativePluginPrerequisiteMissingError, CONFIG_DIR, CONFIG_PATH, OPENCLAW_CONFIG_PATH, ANSI_PATTERN, OPENCLAW_MIN_VERSION, INIT_WIZARD_PLATFORM_REGISTRY, INIT_WIZARD_MENU_SECTIONS, INIT_WIZARD_SELECTION_MAX, PLATFORM_BY_SELECTION, HOSTED_PROXY_PLATFORMS_WITH_URL_KEY, DEFAULT_SHIELD_API_BASE_URL;
2820
2889
  var init_config = __esm({
2821
2890
  "src/proxy/config.ts"() {
2822
2891
  init_consent();
@@ -2907,6 +2976,14 @@ var init_config = __esm({
2907
2976
  PLATFORM_BY_SELECTION = Object.fromEntries(
2908
2977
  INIT_WIZARD_PLATFORM_REGISTRY.map((e, i) => [i + 1, e.slug])
2909
2978
  );
2979
+ HOSTED_PROXY_PLATFORMS_WITH_URL_KEY = /* @__PURE__ */ new Set([
2980
+ "cursor",
2981
+ "claude-desktop",
2982
+ "github-copilot",
2983
+ "kilo-code",
2984
+ "continue-dev",
2985
+ "goose"
2986
+ ]);
2910
2987
  DEFAULT_SHIELD_API_BASE_URL = "https://api.multicorn.ai";
2911
2988
  }
2912
2989
  });
@@ -1635,6 +1635,49 @@ async function createProxyConfig(baseUrl, apiKey, agentName, targetUrl, serverNa
1635
1635
  const data = envelope["data"];
1636
1636
  return typeof data?.["proxy_url"] === "string" ? data["proxy_url"] : "";
1637
1637
  }
1638
+ var HOSTED_PROXY_PLATFORMS_WITH_URL_KEY = /* @__PURE__ */ new Set([
1639
+ "cursor",
1640
+ "claude-desktop",
1641
+ "github-copilot",
1642
+ "kilo-code",
1643
+ "continue-dev",
1644
+ "goose"
1645
+ ]);
1646
+ function shouldEmbedKeyInHostedProxyUrl(platform) {
1647
+ return HOSTED_PROXY_PLATFORMS_WITH_URL_KEY.has(platform);
1648
+ }
1649
+ function hostedProxyUrlWithKeyParam(proxyUrl, apiKey) {
1650
+ if (apiKey.length === 0) {
1651
+ process.stderr.write(
1652
+ style.yellow("\u26A0") + " Could not add key to proxy URL: API key is empty; using URL without key query parameter.\n"
1653
+ );
1654
+ return proxyUrl;
1655
+ }
1656
+ try {
1657
+ const u = new URL(proxyUrl);
1658
+ u.searchParams.set("key", apiKey);
1659
+ return u.toString();
1660
+ } catch (err) {
1661
+ const detail = err instanceof Error ? err.message : String(err);
1662
+ process.stderr.write(
1663
+ style.yellow("\u26A0") + " Could not parse proxy URL to append key query parameter; using URL unchanged. " + style.dim(detail) + "\n"
1664
+ );
1665
+ return proxyUrl;
1666
+ }
1667
+ }
1668
+ function formatHostedProxyUrlForStderr(platform, proxyUrl, apiKey) {
1669
+ if (!shouldEmbedKeyInHostedProxyUrl(platform) || apiKey.length === 0) {
1670
+ return proxyUrl;
1671
+ }
1672
+ try {
1673
+ const u = new URL(proxyUrl);
1674
+ const redactedLabel = apiKey.length <= 4 ? "****" : `mcs_...${apiKey.slice(-4)}`;
1675
+ u.searchParams.set("key", redactedLabel);
1676
+ return u.toString();
1677
+ } catch {
1678
+ return proxyUrl;
1679
+ }
1680
+ }
1638
1681
  function writeMcpAddedLine(shortName, filePath) {
1639
1682
  process.stderr.write(
1640
1683
  style.green("\u2713") + ' MCP server "' + shortName + '" added to ' + style.cyan(filePath) + "\n"
@@ -1784,8 +1827,9 @@ function printHostedProxyPostWriteHints(platform, shortName) {
1784
1827
  }
1785
1828
  async function applyHostedProxyMcpConfig(platform, proxyUrl, shortName, apiKey, workspacePath) {
1786
1829
  const authHeader = `Bearer ${apiKey}`;
1830
+ const proxyUrlWithKeyWhenNeeded = shouldEmbedKeyInHostedProxyUrl(platform) ? hostedProxyUrlWithKeyParam(proxyUrl, apiKey) : proxyUrl;
1787
1831
  if (platform === "gemini-cli") {
1788
- await mergeGeminiHostedMcpServersIntoSettings(shortName, proxyUrl, apiKey);
1832
+ await mergeGeminiHostedMcpServersIntoSettings(shortName, proxyUrlWithKeyWhenNeeded, apiKey);
1789
1833
  process.stderr.write(
1790
1834
  style.dim(
1791
1835
  "For project-specific config, copy the mcpServers entry into .gemini/settings.json in your project root. Restart Gemini CLI if it is already running."
@@ -1810,20 +1854,24 @@ async function applyHostedProxyMcpConfig(platform, proxyUrl, shortName, apiKey,
1810
1854
  let result = "parse-error";
1811
1855
  if (platform === "cursor") {
1812
1856
  result = await mergeMcpServersObjectStyle(getCursorMcpJsonPath(), shortName, {
1813
- url: proxyUrl,
1857
+ url: proxyUrlWithKeyWhenNeeded,
1814
1858
  headers: { Authorization: authHeader }
1815
1859
  });
1816
1860
  if (result === "parse-error") {
1817
1861
  printHostedProxyJsonParseWarning(getCursorMcpJsonPath());
1818
1862
  }
1819
1863
  } else if (platform === "claude-desktop") {
1820
- result = await mergeClaudeDesktopHostedMcpRemote(shortName, proxyUrl, apiKey);
1864
+ result = await mergeClaudeDesktopHostedMcpRemote(
1865
+ shortName,
1866
+ proxyUrlWithKeyWhenNeeded,
1867
+ apiKey
1868
+ );
1821
1869
  if (result === "parse-error") {
1822
1870
  printHostedProxyJsonParseWarning(getClaudeDesktopConfigPath());
1823
1871
  }
1824
1872
  } else if (platform === "windsurf") {
1825
1873
  result = await mergeMcpServersObjectStyle(getWindsurfMcpConfigPath(), shortName, {
1826
- serverUrl: proxyUrl,
1874
+ serverUrl: proxyUrlWithKeyWhenNeeded,
1827
1875
  headers: { Authorization: authHeader }
1828
1876
  });
1829
1877
  if (result === "parse-error") {
@@ -1831,25 +1879,30 @@ async function applyHostedProxyMcpConfig(platform, proxyUrl, shortName, apiKey,
1831
1879
  }
1832
1880
  } else if (platform === "cline") {
1833
1881
  result = await mergeMcpServersObjectStyle(getClineMcpSettingsPath(), shortName, {
1834
- url: proxyUrl,
1882
+ url: proxyUrlWithKeyWhenNeeded,
1835
1883
  headers: { Authorization: authHeader }
1836
1884
  });
1837
1885
  if (result === "parse-error") {
1838
1886
  printHostedProxyJsonParseWarning(getClineMcpSettingsPath());
1839
1887
  }
1840
1888
  } else if (platform === "kilo-code") {
1841
- result = await mergeKiloCodeProjectMcp(workspacePath, shortName, proxyUrl, apiKey);
1889
+ result = await mergeKiloCodeProjectMcp(
1890
+ workspacePath,
1891
+ shortName,
1892
+ proxyUrlWithKeyWhenNeeded,
1893
+ apiKey
1894
+ );
1842
1895
  if (result === "parse-error") {
1843
1896
  printHostedProxyJsonParseWarning(join(workspacePath, ".kilocode", "mcp.json"));
1844
1897
  }
1845
1898
  } else if (platform === "continue-dev") {
1846
- result = await mergeContinueHostedMcp(shortName, proxyUrl, apiKey);
1899
+ result = await mergeContinueHostedMcp(shortName, proxyUrlWithKeyWhenNeeded, apiKey);
1847
1900
  if (result === "parse-error") {
1848
1901
  printHostedProxyJsonParseWarning(getContinueConfigJsonPath());
1849
1902
  }
1850
1903
  } else {
1851
1904
  result = await mergeMcpServersObjectStyle(getCursorMcpJsonPath(), shortName, {
1852
- url: proxyUrl,
1905
+ url: proxyUrlWithKeyWhenNeeded,
1853
1906
  headers: { Authorization: authHeader }
1854
1907
  });
1855
1908
  if (result === "parse-error") {
@@ -1894,6 +1947,7 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
1894
1947
  ]);
1895
1948
  const usesInlineKey = hostedInlinePlatforms.has(platform);
1896
1949
  const authHeader = usesInlineKey ? `Bearer ${apiKey}` : "Bearer YOUR_SHIELD_API_KEY";
1950
+ const urlInSnippet = usesInlineKey && shouldEmbedKeyInHostedProxyUrl(platform) ? hostedProxyUrlWithKeyParam(routingToken, apiKey) : routingToken;
1897
1951
  let snippetText;
1898
1952
  if (platform === "github-copilot") {
1899
1953
  snippetText = JSON.stringify(
@@ -1902,7 +1956,7 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
1902
1956
  servers: {
1903
1957
  [shortName]: {
1904
1958
  type: "http",
1905
- url: routingToken,
1959
+ url: urlInSnippet,
1906
1960
  headers: {
1907
1961
  Authorization: authHeader
1908
1962
  }
@@ -1914,13 +1968,13 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
1914
1968
  2
1915
1969
  );
1916
1970
  } else if (platform === "goose") {
1917
- snippetText = gooseHostedProxyYaml(shortName, routingToken, authHeader);
1971
+ snippetText = gooseHostedProxyYaml(shortName, urlInSnippet, authHeader);
1918
1972
  } else if (platform === "gemini-cli") {
1919
1973
  snippetText = JSON.stringify(
1920
1974
  {
1921
1975
  mcpServers: {
1922
1976
  [shortName]: {
1923
- httpUrl: routingToken,
1977
+ httpUrl: urlInSnippet,
1924
1978
  headers: {
1925
1979
  Authorization: authHeader
1926
1980
  }
@@ -1936,7 +1990,7 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
1936
1990
  mcpServers: {
1937
1991
  [shortName]: {
1938
1992
  command: "npx",
1939
- args: ["-y", "mcp-remote", routingToken, "--header", `Authorization: ${authHeader}`]
1993
+ args: ["-y", "mcp-remote", urlInSnippet, "--header", `Authorization: ${authHeader}`]
1940
1994
  }
1941
1995
  }
1942
1996
  },
@@ -1950,7 +2004,7 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
1950
2004
  {
1951
2005
  name: shortName,
1952
2006
  type: "streamable-http",
1953
- url: routingToken,
2007
+ url: urlInSnippet,
1954
2008
  headers: {
1955
2009
  Authorization: authHeader
1956
2010
  }
@@ -1966,7 +2020,7 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
1966
2020
  {
1967
2021
  mcpServers: {
1968
2022
  [shortName]: {
1969
- [urlKey]: routingToken,
2023
+ [urlKey]: urlInSnippet,
1970
2024
  headers: {
1971
2025
  Authorization: authHeader
1972
2026
  }
@@ -2059,31 +2113,41 @@ function printPlatformSnippet(platform, routingToken, shortName, apiKey) {
2059
2113
  process.stderr.write(style.dim("Start a new Goose session after updating config.") + "\n");
2060
2114
  }
2061
2115
  }
2062
- function dedupeAgentsByName(agents) {
2063
- const seen = /* @__PURE__ */ new Set();
2064
- const out = [];
2065
- for (const a of agents) {
2066
- if (seen.has(a.name)) continue;
2067
- seen.add(a.name);
2068
- out.push(a);
2116
+ function agentDisplayNameDedupeKey(name) {
2117
+ return name.trim().toLowerCase();
2118
+ }
2119
+ function normalizeAgentEntryForMerge(a) {
2120
+ const name = a.name.trim();
2121
+ const ws = typeof a.workspacePath === "string" && a.workspacePath.length > 0 ? a.workspacePath : void 0;
2122
+ return ws !== void 0 ? { name, platform: a.platform, workspacePath: ws } : { name, platform: a.platform };
2123
+ }
2124
+ function mergeAgentEntryDupPair(first, second) {
2125
+ const name = first.name.trim();
2126
+ const platform = first.platform;
2127
+ const ws = typeof first.workspacePath === "string" && first.workspacePath.length > 0 ? first.workspacePath : typeof second.workspacePath === "string" && second.workspacePath.length > 0 ? second.workspacePath : void 0;
2128
+ return ws !== void 0 ? { name, platform, workspacePath: ws } : { name, platform };
2129
+ }
2130
+ function mergeAgentsForUniqueNames(agents) {
2131
+ const byKey = /* @__PURE__ */ new Map();
2132
+ for (const raw of agents) {
2133
+ const key = agentDisplayNameDedupeKey(raw.name);
2134
+ const candidate = normalizeAgentEntryForMerge(raw);
2135
+ const prev = byKey.get(key);
2136
+ byKey.set(key, prev === void 0 ? candidate : mergeAgentEntryDupPair(prev, candidate));
2069
2137
  }
2070
- return out;
2138
+ return [...byKey.values()];
2071
2139
  }
2072
2140
  function mergeAgentsForPlatform(localAgents, remoteAgents, selectedPlatform) {
2073
- const byName = /* @__PURE__ */ new Map();
2141
+ const merged = [];
2074
2142
  for (const a of localAgents) {
2075
2143
  if (a.platform !== selectedPlatform) continue;
2076
- if (!byName.has(a.name)) {
2077
- byName.set(a.name, { ...a });
2078
- }
2144
+ merged.push(a);
2079
2145
  }
2080
2146
  for (const r of remoteAgents) {
2081
2147
  if (r.platform !== selectedPlatform) continue;
2082
- if (!byName.has(r.name)) {
2083
- byName.set(r.name, { name: r.name, platform: selectedPlatform });
2084
- }
2148
+ merged.push({ name: r.name, platform: selectedPlatform });
2085
2149
  }
2086
- return [...byName.values()];
2150
+ return mergeAgentsForUniqueNames(merged);
2087
2151
  }
2088
2152
  var DEFAULT_SHIELD_API_BASE_URL = "https://api.multicorn.ai";
2089
2153
  async function runInit(explicitBaseUrl, options) {
@@ -2223,8 +2287,10 @@ async function runInit(explicitBaseUrl, options) {
2223
2287
  continue;
2224
2288
  }
2225
2289
  const remoteAccountAgents = await fetchRemoteAgentsSummaries(apiKey, resolvedBaseUrl);
2226
- const agentsForPlatform = dedupeAgentsByName(
2227
- mergeAgentsForPlatform(currentAgents, remoteAccountAgents, selectedPlatform)
2290
+ const agentsForPlatform = mergeAgentsForPlatform(
2291
+ currentAgents,
2292
+ remoteAccountAgents,
2293
+ selectedPlatform
2228
2294
  );
2229
2295
  const localForPlatformCount = currentAgents.filter(
2230
2296
  (a) => a.platform === selectedPlatform
@@ -2486,7 +2552,9 @@ You have ${String(agentsForPlatform.length)} agent(s) connected for ${selectedLa
2486
2552
  }
2487
2553
  if (created && proxyUrl.length > 0) {
2488
2554
  process.stderr.write("\n" + style.bold("Your Shield proxy URL:") + "\n");
2489
- process.stderr.write(" " + style.cyan(proxyUrl) + "\n");
2555
+ process.stderr.write(
2556
+ " " + style.cyan(formatHostedProxyUrlForStderr(selectedPlatform, proxyUrl, apiKey)) + "\n"
2557
+ );
2490
2558
  await applyHostedProxyMcpConfig(
2491
2559
  selectedPlatform,
2492
2560
  proxyUrl,
@@ -2577,7 +2645,9 @@ You have ${String(agentsForPlatform.length)} agent(s) connected for ${selectedLa
2577
2645
  }
2578
2646
  if (created && proxyUrl.length > 0) {
2579
2647
  process.stderr.write("\n" + style.bold("Your Shield proxy URL:") + "\n");
2580
- process.stderr.write(" " + style.cyan(proxyUrl) + "\n");
2648
+ process.stderr.write(
2649
+ " " + style.cyan(formatHostedProxyUrlForStderr(selectedPlatform, proxyUrl, apiKey)) + "\n"
2650
+ );
2581
2651
  await applyHostedProxyMcpConfig(
2582
2652
  selectedPlatform,
2583
2653
  proxyUrl,
@@ -2662,7 +2732,9 @@ You have ${String(agentsForPlatform.length)} agent(s) connected for ${selectedLa
2662
2732
  }
2663
2733
  if (created && proxyUrl.length > 0) {
2664
2734
  process.stderr.write("\n" + style.bold("Your Shield proxy URL:") + "\n");
2665
- process.stderr.write(" " + style.cyan(proxyUrl) + "\n");
2735
+ process.stderr.write(
2736
+ " " + style.cyan(formatHostedProxyUrlForStderr(selectedPlatform, proxyUrl, apiKey)) + "\n"
2737
+ );
2666
2738
  await applyHostedProxyMcpConfig(
2667
2739
  selectedPlatform,
2668
2740
  proxyUrl,
@@ -2710,7 +2782,9 @@ You have ${String(agentsForPlatform.length)} agent(s) connected for ${selectedLa
2710
2782
  }
2711
2783
  if (created && proxyUrl.length > 0) {
2712
2784
  process.stderr.write("\n" + style.bold("Your Shield proxy URL:") + "\n");
2713
- process.stderr.write(" " + style.cyan(proxyUrl) + "\n");
2785
+ process.stderr.write(
2786
+ " " + style.cyan(formatHostedProxyUrlForStderr(selectedPlatform, proxyUrl, apiKey)) + "\n"
2787
+ );
2714
2788
  await applyHostedProxyMcpConfig(
2715
2789
  selectedPlatform,
2716
2790
  proxyUrl,
@@ -2731,7 +2805,10 @@ You have ${String(agentsForPlatform.length)} agent(s) connected for ${selectedLa
2731
2805
  }
2732
2806
  if (setupSucceeded) {
2733
2807
  if (removeAgentNameBeforeSave !== void 0) {
2734
- currentAgents = currentAgents.filter((a) => a.name !== removeAgentNameBeforeSave);
2808
+ const removeKey = agentDisplayNameDedupeKey(removeAgentNameBeforeSave);
2809
+ currentAgents = currentAgents.filter(
2810
+ (a) => agentDisplayNameDedupeKey(a.name) !== removeKey
2811
+ );
2735
2812
  }
2736
2813
  currentAgents.push({
2737
2814
  name: agentName,
@@ -22417,7 +22417,7 @@ async function writeExtensionBackup(claudeDesktopConfigPath, mcpServers) {
22417
22417
 
22418
22418
  // package.json
22419
22419
  var package_default = {
22420
- version: "1.3.4"};
22420
+ version: "1.3.6"};
22421
22421
 
22422
22422
  // src/package-meta.ts
22423
22423
  var PACKAGE_VERSION = package_default.version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multicorn-shield",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "description": "The control layer for AI agents: permissions, consent, spending limits, and audit logging.",
5
5
  "license": "MIT",
6
6
  "author": "Multicorn AI Pty Ltd",