@xmemo/client 0.4.137 → 0.4.138
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/README.md +14 -11
- package/package.json +1 -1
- package/src/cli.js +106 -21
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# XMemo CLI
|
|
2
2
|
|
|
3
|
+
[](https://smithery.ai/servers/xmemo/xmemo)
|
|
4
|
+
|
|
3
5
|
`@xmemo/client` is the privacy-first command line entry point for XMemo client
|
|
4
6
|
setup. It is intentionally small: the npm package contains only the CLI and
|
|
5
7
|
setup helper code needed on a user's machine.
|
|
@@ -235,7 +237,7 @@ xmemo setup copilot
|
|
|
235
237
|
xmemo mcp proxy
|
|
236
238
|
```
|
|
237
239
|
|
|
238
|
-
`xmemo setup copilot` writes `
|
|
240
|
+
`xmemo setup copilot` writes `XMemo` to Copilot CLI's user MCP config and
|
|
239
241
|
does not include token or identity headers. Use `xmemo setup copilot --dry-run`
|
|
240
242
|
to preview without writing. `xmemo mcp proxy` reads the token saved by
|
|
241
243
|
`xmemo login` or `xmemo token add --from-stdin`, adds the XMemo bearer token and
|
|
@@ -285,9 +287,9 @@ Write it to the default Codex config path:
|
|
|
285
287
|
xmemo mcp add codex --url "$XMEMO_URL" --write
|
|
286
288
|
```
|
|
287
289
|
|
|
288
|
-
The generated config references `XMEMO_KEY
|
|
289
|
-
|
|
290
|
-
|
|
290
|
+
The generated config references `XMEMO_KEY`, includes the non-secret
|
|
291
|
+
`X-Memory-OS-Agent-ID` / `X-Memory-OS-Agent-Instance-ID` attribution headers,
|
|
292
|
+
and does not include the token value.
|
|
291
293
|
|
|
292
294
|
Codex MCP-depth checks:
|
|
293
295
|
|
|
@@ -302,7 +304,7 @@ xmemo smoke --client codex
|
|
|
302
304
|
`xmemo mcp profile codex` prints the recommended memory behavior profile:
|
|
303
305
|
recall/search at the start of non-trivial tasks, write back high-signal
|
|
304
306
|
decisions and fixes, and never store secrets. `xmemo smoke --client codex`
|
|
305
|
-
checks the local Codex TOML config for the `
|
|
307
|
+
checks the local Codex TOML config for the `XMemo` MCP server,
|
|
306
308
|
`bearer_token_env_var = "XMEMO_KEY"`, token presence in the environment, and
|
|
307
309
|
absence of embedded token values.
|
|
308
310
|
|
|
@@ -322,8 +324,9 @@ lower-level equivalent remains:
|
|
|
322
324
|
xmemo mcp add cursor --url "$XMEMO_URL" --write
|
|
323
325
|
```
|
|
324
326
|
|
|
325
|
-
The CLI refuses to overwrite an existing `memory_os
|
|
326
|
-
config manually if you need to rotate the endpoint.
|
|
327
|
+
The CLI refuses to overwrite an existing `XMemo`, `memory_os`, or `memory-os`
|
|
328
|
+
MCP server entry. Edit the config manually if you need to rotate the endpoint.
|
|
329
|
+
Cursor configs include
|
|
327
330
|
`X-Memory-OS-Agent-ID` and `X-Memory-OS-Agent-Instance-ID`; the instance ID is
|
|
328
331
|
non-secret and stored under the user's XMemo CLI config directory. By default,
|
|
329
332
|
the setup prompt also installs a Cursor behavior profile at
|
|
@@ -352,8 +355,8 @@ run `/mcp` (or the first XMemo tool call) to complete the OAuth login. By
|
|
|
352
355
|
default, the setup prompt also installs a Gemini behavior profile at
|
|
353
356
|
`~/.gemini/GEMINI.md`; answer `n` or pass `--no-profile` to skip it.
|
|
354
357
|
|
|
355
|
-
The CLI refuses to overwrite an existing `memory_os
|
|
356
|
-
config manually if you need to rotate the endpoint.
|
|
358
|
+
The CLI refuses to overwrite an existing `XMemo`, `memory_os`, or `memory-os`
|
|
359
|
+
MCP server entry. Edit the config manually if you need to rotate the endpoint.
|
|
357
360
|
|
|
358
361
|
### Antigravity
|
|
359
362
|
|
|
@@ -381,8 +384,8 @@ Use `xmemo setup antigravity` for normal installs because it performs discovery
|
|
|
381
384
|
and chooses the recommended Antigravity path automatically. Use
|
|
382
385
|
`xmemo mcp add antigravity --write` when you want the generic MCP writer
|
|
383
386
|
directly, for example with `--url` or `--config` in advanced/multi-client setup.
|
|
384
|
-
The CLI refuses to overwrite an existing `memory_os
|
|
385
|
-
config manually if you need to rotate the endpoint.
|
|
387
|
+
The CLI refuses to overwrite an existing `XMemo`, `memory_os`, or `memory-os`
|
|
388
|
+
MCP server entry. Edit the config manually if you need to rotate the endpoint.
|
|
386
389
|
|
|
387
390
|
## Release model
|
|
388
391
|
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ const PACKAGE_NAME = '@xmemo/client';
|
|
|
10
10
|
const FALLBACK_PACKAGE_NAME = '@yonro/xmemo-client';
|
|
11
11
|
const COMMAND_NAME = 'xmemo';
|
|
12
12
|
const LEGACY_COMMAND_NAME = 'memory-os';
|
|
13
|
-
const CLI_VERSION = '0.4.
|
|
13
|
+
const CLI_VERSION = '0.4.138';
|
|
14
14
|
const DEFAULT_SERVICE_URL = 'https://xmemo.dev';
|
|
15
15
|
const TOKEN_ENV_VAR = 'XMEMO_KEY';
|
|
16
16
|
const LEGACY_TOKEN_ENV_VAR = 'MEMORY_OS_MCP_TOKEN';
|
|
@@ -18,7 +18,8 @@ const AGENT_ID_ENV_VAR = 'XMEMO_AGENT_ID';
|
|
|
18
18
|
const AGENT_INSTANCE_ENV_VAR = 'XMEMO_AGENT_INSTANCE_ID';
|
|
19
19
|
const AGENT_ID_HEADER = 'X-Memory-OS-Agent-ID';
|
|
20
20
|
const AGENT_INSTANCE_HEADER = 'X-Memory-OS-Agent-Instance-ID';
|
|
21
|
-
const MCP_SERVER_NAME = '
|
|
21
|
+
const MCP_SERVER_NAME = 'XMemo';
|
|
22
|
+
const LEGACY_MCP_SERVER_NAMES = ['memory_os', 'memory-os'];
|
|
22
23
|
const CODEX_PROFILE_TARGET = 'AGENTS.md';
|
|
23
24
|
const CODEX_PROFILE_MARKER_START = '<!-- memory-os:codex-profile:start -->';
|
|
24
25
|
const CODEX_PROFILE_MARKER_END = '<!-- memory-os:codex-profile:end -->';
|
|
@@ -190,7 +191,7 @@ function writeHelp(io) {
|
|
|
190
191
|
writeLine(io.stdout, ` ${COMMAND_NAME} token add --from-stdin`);
|
|
191
192
|
writeLine(io.stdout, ` ${COMMAND_NAME} token set --from-stdin [--allow-plaintext]`);
|
|
192
193
|
writeLine(io.stdout, ` ${COMMAND_NAME} mcp list`);
|
|
193
|
-
writeLine(io.stdout, ` ${COMMAND_NAME} mcp config --client <codex|cursor|copilot-cli|antigravity|generic> [--base-url <url>] [--json]`);
|
|
194
|
+
writeLine(io.stdout, ` ${COMMAND_NAME} mcp config --client <codex|cursor|copilot-cli|gemini-cli|antigravity|generic> [--base-url <url>] [--json]`);
|
|
194
195
|
writeLine(io.stdout, ` ${COMMAND_NAME} mcp proxy [--port ${DEFAULT_PROXY_PORT}]`);
|
|
195
196
|
writeLine(io.stdout, ` ${COMMAND_NAME} mcp profile codex [--json]`);
|
|
196
197
|
writeLine(io.stdout, ` ${COMMAND_NAME} profile install <codex|cursor|gemini|antigravity> [--target <path>] [--dry-run|--json]`);
|
|
@@ -779,6 +780,15 @@ async function mcpCommand(args, io) {
|
|
|
779
780
|
} else {
|
|
780
781
|
writeLine(io.stdout, JSON.stringify(template.snippet, null, 2));
|
|
781
782
|
}
|
|
783
|
+
if (template.optionalEnv?.includes(AGENT_INSTANCE_ENV_VAR)) {
|
|
784
|
+
writeLine(io.stdout, '');
|
|
785
|
+
writeLine(io.stdout, `${AGENT_INSTANCE_ENV_VAR} must be stable per local client install.`);
|
|
786
|
+
if (template.agentInstanceGeneration?.automaticCommand) {
|
|
787
|
+
writeLine(io.stdout, `Use ${template.agentInstanceGeneration.automaticCommand} to generate and persist it, or set it to a unique value such as xmemo-${clientId}-<uuid>.`);
|
|
788
|
+
} else {
|
|
789
|
+
writeLine(io.stdout, `Set it to a unique value such as xmemo-${clientId}-<uuid> and persist it outside git.`);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
782
792
|
writeLine(io.stdout, 'Review the template before applying it. Token values are not included.');
|
|
783
793
|
return 0;
|
|
784
794
|
}
|
|
@@ -829,6 +839,7 @@ async function mcpCommand(args, io) {
|
|
|
829
839
|
agentId: identity.agentId,
|
|
830
840
|
agentInstanceId: identity.agentInstanceId,
|
|
831
841
|
agentInstanceIdPath: identity.path,
|
|
842
|
+
agentInstanceGeneration: agentInstanceGenerationPolicy(target),
|
|
832
843
|
writesTokenValue: false
|
|
833
844
|
}, null, 2));
|
|
834
845
|
return 0;
|
|
@@ -857,6 +868,7 @@ async function mcpCommand(args, io) {
|
|
|
857
868
|
} else {
|
|
858
869
|
writeLine(io.stdout, `Set ${TOKEN_ENV_VAR} in your user environment or secret manager. The token value is not included here.`);
|
|
859
870
|
}
|
|
871
|
+
writeLine(io.stdout, `${AGENT_INSTANCE_ENV_VAR} must be stable per local ${client.label} install; run ${COMMAND_NAME} mcp add ${target} --write to generate it automatically.`);
|
|
860
872
|
return 0;
|
|
861
873
|
}
|
|
862
874
|
|
|
@@ -1389,10 +1401,15 @@ function mcpConfigTemplate(clientId, mcpUrl) {
|
|
|
1389
1401
|
agentInstanceEnvVar: AGENT_INSTANCE_ENV_VAR,
|
|
1390
1402
|
agentInstanceHeader: AGENT_INSTANCE_HEADER
|
|
1391
1403
|
},
|
|
1404
|
+
agentInstanceGeneration: agentInstanceGenerationPolicy(clientId),
|
|
1392
1405
|
writesTokenValue: false
|
|
1393
1406
|
};
|
|
1394
1407
|
}
|
|
1395
1408
|
|
|
1409
|
+
if (clientId === 'cursor') {
|
|
1410
|
+
return bearerJsonMcpTemplate(clientId, mcpUrl, cursorJsonConfig(mcpUrl));
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1396
1413
|
if (clientId === 'gemini-cli') {
|
|
1397
1414
|
return oauthJsonMcpTemplate(clientId, mcpUrl, geminiJsonConfig(mcpUrl));
|
|
1398
1415
|
}
|
|
@@ -1401,14 +1418,13 @@ function mcpConfigTemplate(clientId, mcpUrl) {
|
|
|
1401
1418
|
return oauthJsonMcpTemplate(clientId, mcpUrl, antigravityJsonConfig(mcpUrl));
|
|
1402
1419
|
}
|
|
1403
1420
|
|
|
1404
|
-
const serverName = clientId === 'cursor' || clientId === 'gemini-cli' || clientId === 'antigravity' ? 'memory_os' : 'memory-os';
|
|
1405
1421
|
return {
|
|
1406
1422
|
client: clientId,
|
|
1407
|
-
serverName,
|
|
1423
|
+
serverName: MCP_SERVER_NAME,
|
|
1408
1424
|
snippetFormat: 'json',
|
|
1409
1425
|
snippet: {
|
|
1410
1426
|
mcpServers: {
|
|
1411
|
-
[
|
|
1427
|
+
[MCP_SERVER_NAME]: {
|
|
1412
1428
|
type: 'http',
|
|
1413
1429
|
url: mcpUrl,
|
|
1414
1430
|
headers: {
|
|
@@ -1427,6 +1443,28 @@ function mcpConfigTemplate(clientId, mcpUrl) {
|
|
|
1427
1443
|
agentInstanceEnvVar: AGENT_INSTANCE_ENV_VAR,
|
|
1428
1444
|
agentInstanceHeader: AGENT_INSTANCE_HEADER
|
|
1429
1445
|
},
|
|
1446
|
+
agentInstanceGeneration: agentInstanceGenerationPolicy(clientId),
|
|
1447
|
+
writesTokenValue: false
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
function bearerJsonMcpTemplate(clientId, mcpUrl, snippet) {
|
|
1452
|
+
return {
|
|
1453
|
+
client: clientId,
|
|
1454
|
+
serverName: MCP_SERVER_NAME,
|
|
1455
|
+
snippetFormat: 'json',
|
|
1456
|
+
snippet,
|
|
1457
|
+
requiresEnv: [TOKEN_ENV_VAR],
|
|
1458
|
+
optionalEnv: [AGENT_INSTANCE_ENV_VAR],
|
|
1459
|
+
authentication: 'env-bearer',
|
|
1460
|
+
agentIdentity: {
|
|
1461
|
+
agentId: clientId,
|
|
1462
|
+
agentIdHeader: AGENT_ID_HEADER,
|
|
1463
|
+
agentInstanceEnvVar: AGENT_INSTANCE_ENV_VAR,
|
|
1464
|
+
agentInstanceHeader: AGENT_INSTANCE_HEADER
|
|
1465
|
+
},
|
|
1466
|
+
agentInstanceGeneration: agentInstanceGenerationPolicy(clientId),
|
|
1467
|
+
mcpUrl,
|
|
1430
1468
|
writesTokenValue: false
|
|
1431
1469
|
};
|
|
1432
1470
|
}
|
|
@@ -1446,20 +1484,20 @@ function oauthJsonMcpTemplate(clientId, mcpUrl, snippet) {
|
|
|
1446
1484
|
agentInstanceEnvVar: AGENT_INSTANCE_ENV_VAR,
|
|
1447
1485
|
agentInstanceHeader: AGENT_INSTANCE_HEADER
|
|
1448
1486
|
},
|
|
1487
|
+
agentInstanceGeneration: agentInstanceGenerationPolicy(clientId),
|
|
1449
1488
|
mcpUrl,
|
|
1450
1489
|
writesTokenValue: false
|
|
1451
1490
|
};
|
|
1452
1491
|
}
|
|
1453
1492
|
|
|
1454
1493
|
function mcpLocalProxyTemplate(clientId, proxyUrl) {
|
|
1455
|
-
const serverName = clientId === 'cursor' || clientId === 'gemini-cli' || clientId === 'antigravity' ? 'memory_os' : 'memory-os';
|
|
1456
1494
|
return {
|
|
1457
1495
|
client: clientId,
|
|
1458
|
-
serverName,
|
|
1496
|
+
serverName: MCP_SERVER_NAME,
|
|
1459
1497
|
snippetFormat: 'json',
|
|
1460
1498
|
snippet: {
|
|
1461
1499
|
mcpServers: {
|
|
1462
|
-
[
|
|
1500
|
+
[MCP_SERVER_NAME]: {
|
|
1463
1501
|
type: 'http',
|
|
1464
1502
|
url: proxyUrl
|
|
1465
1503
|
}
|
|
@@ -1473,10 +1511,27 @@ function mcpLocalProxyTemplate(clientId, proxyUrl) {
|
|
|
1473
1511
|
agentInstanceEnvVar: AGENT_INSTANCE_ENV_VAR,
|
|
1474
1512
|
agentInstanceHeader: AGENT_INSTANCE_HEADER
|
|
1475
1513
|
},
|
|
1514
|
+
agentInstanceGeneration: agentInstanceGenerationPolicy(clientId),
|
|
1476
1515
|
writesTokenValue: false
|
|
1477
1516
|
};
|
|
1478
1517
|
}
|
|
1479
1518
|
|
|
1519
|
+
function agentInstanceGenerationPolicy(clientId) {
|
|
1520
|
+
const automaticCommand = MCP_CLIENTS.has(clientId)
|
|
1521
|
+
? `${COMMAND_NAME} mcp add ${clientId} --write`
|
|
1522
|
+
: clientId === 'copilot-cli'
|
|
1523
|
+
? `${COMMAND_NAME} setup copilot --write`
|
|
1524
|
+
: null;
|
|
1525
|
+
return {
|
|
1526
|
+
requiredForHeaders: true,
|
|
1527
|
+
stablePerInstall: true,
|
|
1528
|
+
automaticCommand,
|
|
1529
|
+
generatedPattern: `xmemo-${clientId}-<uuid>`,
|
|
1530
|
+
storagePath: `~/.config/xmemo/agent-instances/${clientId}.json`,
|
|
1531
|
+
manualEnvVar: AGENT_INSTANCE_ENV_VAR
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1480
1535
|
function sameMajorMinor(left, right) {
|
|
1481
1536
|
const leftParts = left.split('.');
|
|
1482
1537
|
const rightParts = right.split('.');
|
|
@@ -2079,7 +2134,8 @@ function writeProfileResult(action, result, io) {
|
|
|
2079
2134
|
|
|
2080
2135
|
async function codexSmokeReport(configPath, env) {
|
|
2081
2136
|
const configText = await readTextIfExists(configPath);
|
|
2082
|
-
const
|
|
2137
|
+
const serverBlock = findTomlServerBlock(configText);
|
|
2138
|
+
const block = serverBlock.block;
|
|
2083
2139
|
const mcpUrl = block ? tomlStringValue(block, 'url') : null;
|
|
2084
2140
|
const bearerTokenEnvVar = block ? tomlStringValue(block, 'bearer_token_env_var') : null;
|
|
2085
2141
|
const tokenValue = env[TOKEN_ENV_VAR] ?? '';
|
|
@@ -2096,7 +2152,7 @@ async function codexSmokeReport(configPath, env) {
|
|
|
2096
2152
|
name: 'memory_os_server_present',
|
|
2097
2153
|
ok: Boolean(block),
|
|
2098
2154
|
required: true,
|
|
2099
|
-
detail: block ? `[mcp_servers.${
|
|
2155
|
+
detail: block ? `[mcp_servers.${serverBlock.name}]` : `missing [mcp_servers.${MCP_SERVER_NAME}]`
|
|
2100
2156
|
},
|
|
2101
2157
|
{
|
|
2102
2158
|
name: 'mcp_url_present',
|
|
@@ -2134,7 +2190,7 @@ async function codexSmokeReport(configPath, env) {
|
|
|
2134
2190
|
ok: checks.every((check) => !check.required || check.ok),
|
|
2135
2191
|
client: 'codex',
|
|
2136
2192
|
configPath,
|
|
2137
|
-
serverName: MCP_SERVER_NAME,
|
|
2193
|
+
serverName: serverBlock.name ?? MCP_SERVER_NAME,
|
|
2138
2194
|
mcpUrl,
|
|
2139
2195
|
tokenEnvVar: TOKEN_ENV_VAR,
|
|
2140
2196
|
agentInstanceIdPath: identityPath,
|
|
@@ -2142,6 +2198,26 @@ async function codexSmokeReport(configPath, env) {
|
|
|
2142
2198
|
};
|
|
2143
2199
|
}
|
|
2144
2200
|
|
|
2201
|
+
function knownMcpServerNames() {
|
|
2202
|
+
return [MCP_SERVER_NAME, ...LEGACY_MCP_SERVER_NAMES];
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
function existingJsonMcpServerName(mcpServers) {
|
|
2206
|
+
return knownMcpServerNames().find((name) => mcpServers[name]);
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
function existingTomlMcpServerName(content) {
|
|
2210
|
+
return knownMcpServerNames().find((name) => content.includes(`[mcp_servers.${name}]`));
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
function findTomlServerBlock(content) {
|
|
2214
|
+
const name = existingTomlMcpServerName(content);
|
|
2215
|
+
return {
|
|
2216
|
+
name: name ?? null,
|
|
2217
|
+
block: name ? tomlServerBlock(content, name) : ''
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2145
2221
|
function tomlServerBlock(content, serverName) {
|
|
2146
2222
|
const header = `[mcp_servers.${serverName}]`;
|
|
2147
2223
|
const lines = content.split(/\r?\n/);
|
|
@@ -2174,8 +2250,9 @@ function cursorJsonSnippet(mcpUrl, identity = envReferenceIdentity('cursor')) {
|
|
|
2174
2250
|
async function appendTomlServerConfig(configPath, mcpUrl) {
|
|
2175
2251
|
const snippet = codexTomlSnippet(mcpUrl);
|
|
2176
2252
|
const existing = await readTextIfExists(configPath);
|
|
2177
|
-
|
|
2178
|
-
|
|
2253
|
+
const existingName = existingTomlMcpServerName(existing);
|
|
2254
|
+
if (existingName) {
|
|
2255
|
+
throw new UsageError(`MCP config already contains [mcp_servers.${existingName}]. Edit ${configPath} manually to avoid duplicate server definitions.`);
|
|
2179
2256
|
}
|
|
2180
2257
|
|
|
2181
2258
|
await fs.mkdir(path.dirname(configPath), { recursive: true, mode: 0o700 });
|
|
@@ -2196,8 +2273,9 @@ async function mergeJsonMcpConfig(configPath, mcpUrl, identity) {
|
|
|
2196
2273
|
parsed.mcpServers = {};
|
|
2197
2274
|
}
|
|
2198
2275
|
|
|
2199
|
-
|
|
2200
|
-
|
|
2276
|
+
const existingName = existingJsonMcpServerName(parsed.mcpServers);
|
|
2277
|
+
if (existingName) {
|
|
2278
|
+
throw new UsageError(`MCP config already contains mcpServers.${existingName}. Edit ${configPath} manually to avoid duplicate server definitions.`);
|
|
2201
2279
|
}
|
|
2202
2280
|
|
|
2203
2281
|
parsed.mcpServers[MCP_SERVER_NAME] = cursorJsonServerConfig(mcpUrl, identity);
|
|
@@ -2240,8 +2318,9 @@ async function mergeAntigravityMcpConfig(configPath, mcpUrl, identity) {
|
|
|
2240
2318
|
parsed.mcpServers = {};
|
|
2241
2319
|
}
|
|
2242
2320
|
|
|
2243
|
-
|
|
2244
|
-
|
|
2321
|
+
const existingName = existingJsonMcpServerName(parsed.mcpServers);
|
|
2322
|
+
if (existingName) {
|
|
2323
|
+
throw new UsageError(`MCP config already contains mcpServers.${existingName}. Edit ${configPath} manually to avoid duplicate server definitions.`);
|
|
2245
2324
|
}
|
|
2246
2325
|
|
|
2247
2326
|
parsed.mcpServers[MCP_SERVER_NAME] = antigravityJsonServerConfig(mcpUrl, identity);
|
|
@@ -2263,8 +2342,9 @@ async function mergeGeminiMcpConfig(configPath, mcpUrl, identity) {
|
|
|
2263
2342
|
parsed.mcpServers = {};
|
|
2264
2343
|
}
|
|
2265
2344
|
|
|
2266
|
-
|
|
2267
|
-
|
|
2345
|
+
const existingName = existingJsonMcpServerName(parsed.mcpServers);
|
|
2346
|
+
if (existingName) {
|
|
2347
|
+
throw new UsageError(`MCP config already contains mcpServers.${existingName}. Edit ${configPath} manually to avoid duplicate server definitions.`);
|
|
2268
2348
|
}
|
|
2269
2349
|
|
|
2270
2350
|
parsed.mcpServers[MCP_SERVER_NAME] = geminiJsonServerConfig(mcpUrl, identity);
|
|
@@ -2285,7 +2365,12 @@ async function mergeCopilotMcpConfig(configPath, proxyUrl) {
|
|
|
2285
2365
|
parsed.mcpServers = {};
|
|
2286
2366
|
}
|
|
2287
2367
|
|
|
2288
|
-
|
|
2368
|
+
const existingName = existingJsonMcpServerName(parsed.mcpServers);
|
|
2369
|
+
if (existingName) {
|
|
2370
|
+
throw new UsageError(`MCP config already contains mcpServers.${existingName}. Edit ${configPath} manually to avoid duplicate server definitions.`);
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
parsed.mcpServers[MCP_SERVER_NAME] = copilotLocalProxyServerConfig(proxyUrl);
|
|
2289
2374
|
await fs.mkdir(path.dirname(configPath), { recursive: true, mode: 0o700 });
|
|
2290
2375
|
await fs.writeFile(configPath, `${JSON.stringify(parsed, null, 2)}\n`, { mode: 0o600 });
|
|
2291
2376
|
await bestEffortChmod(configPath, 0o600);
|