@agent-native/core 0.51.14 → 0.52.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.
Files changed (64) hide show
  1. package/README.md +3 -3
  2. package/dist/cli/connect.d.ts +4 -3
  3. package/dist/cli/connect.d.ts.map +1 -1
  4. package/dist/cli/connect.js +67 -26
  5. package/dist/cli/connect.js.map +1 -1
  6. package/dist/cli/mcp-config-writers.d.ts +20 -13
  7. package/dist/cli/mcp-config-writers.d.ts.map +1 -1
  8. package/dist/cli/mcp-config-writers.js +152 -13
  9. package/dist/cli/mcp-config-writers.js.map +1 -1
  10. package/dist/cli/mcp.d.ts +2 -2
  11. package/dist/cli/mcp.d.ts.map +1 -1
  12. package/dist/cli/mcp.js +41 -193
  13. package/dist/cli/mcp.js.map +1 -1
  14. package/dist/cli/plan-local.d.ts +13 -1
  15. package/dist/cli/plan-local.d.ts.map +1 -1
  16. package/dist/cli/plan-local.js +62 -8
  17. package/dist/cli/plan-local.js.map +1 -1
  18. package/dist/cli/recap.d.ts.map +1 -1
  19. package/dist/cli/recap.js +1 -1
  20. package/dist/cli/recap.js.map +1 -1
  21. package/dist/cli/skills.d.ts +13 -6
  22. package/dist/cli/skills.d.ts.map +1 -1
  23. package/dist/cli/skills.js +224 -59
  24. package/dist/cli/skills.js.map +1 -1
  25. package/dist/client/agent-engine-key.d.ts +6 -4
  26. package/dist/client/agent-engine-key.d.ts.map +1 -1
  27. package/dist/client/agent-engine-key.js +9 -6
  28. package/dist/client/agent-engine-key.js.map +1 -1
  29. package/dist/client/chat/run-recovery.js +1 -1
  30. package/dist/client/chat/run-recovery.js.map +1 -1
  31. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  32. package/dist/client/settings/SettingsPanel.js +7 -14
  33. package/dist/client/settings/SettingsPanel.js.map +1 -1
  34. package/dist/coding-tools/run-code.d.ts +7 -0
  35. package/dist/coding-tools/run-code.d.ts.map +1 -1
  36. package/dist/coding-tools/run-code.js +21 -106
  37. package/dist/coding-tools/run-code.js.map +1 -1
  38. package/dist/coding-tools/sandbox/adapter.d.ts +79 -0
  39. package/dist/coding-tools/sandbox/adapter.d.ts.map +1 -0
  40. package/dist/coding-tools/sandbox/adapter.js +24 -0
  41. package/dist/coding-tools/sandbox/adapter.js.map +1 -0
  42. package/dist/coding-tools/sandbox/index.d.ts +51 -0
  43. package/dist/coding-tools/sandbox/index.d.ts.map +1 -0
  44. package/dist/coding-tools/sandbox/index.js +79 -0
  45. package/dist/coding-tools/sandbox/index.js.map +1 -0
  46. package/dist/coding-tools/sandbox/local-child-process-adapter.d.ts +24 -0
  47. package/dist/coding-tools/sandbox/local-child-process-adapter.d.ts.map +1 -0
  48. package/dist/coding-tools/sandbox/local-child-process-adapter.js +141 -0
  49. package/dist/coding-tools/sandbox/local-child-process-adapter.js.map +1 -0
  50. package/dist/server/agent-engine-api-key-route.d.ts +37 -0
  51. package/dist/server/agent-engine-api-key-route.d.ts.map +1 -0
  52. package/dist/server/agent-engine-api-key-route.js +105 -0
  53. package/dist/server/agent-engine-api-key-route.js.map +1 -0
  54. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  55. package/dist/server/core-routes-plugin.js +10 -6
  56. package/dist/server/core-routes-plugin.js.map +1 -1
  57. package/dist/server/create-server.js +1 -1
  58. package/dist/server/create-server.js.map +1 -1
  59. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +7 -4
  60. package/docs/content/plan-plugin.md +5 -4
  61. package/docs/content/pr-visual-recap.md +1 -1
  62. package/docs/content/template-plan.md +1 -1
  63. package/package.json +1 -1
  64. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +7 -4
@@ -3,15 +3,18 @@
3
3
  *
4
4
  * Extracted so both `agent-native mcp install` (see `mcp.ts`) and
5
5
  * `agent-native connect` (see `connect.ts`) write the EXACT same on-disk
6
- * config file targets and formats for every supported client. `mcp.ts`
7
- * intentionally keeps its own hand-rolled copies of these writers (its
8
- * external behavior is unchanged); new code should import from here so the
9
- * formats never diverge.
6
+ * config file targets and formats for every supported client.
10
7
  *
11
8
  * Supported clients and their config files:
12
9
  * - claude-code / claude-code-cli → `.mcp.json` (project) or
13
10
  * `~/.claude.json` (user). JSON `mcpServers[name] = entry`.
14
11
  * - cowork → `~/.cowork/mcp.json`. Same JSON shape.
12
+ * - cursor → `.cursor/mcp.json` (project) or
13
+ * `~/.cursor/mcp.json` (user). JSON `mcpServers[name] = entry`.
14
+ * - opencode → `opencode.json` (project) or
15
+ * `~/.config/opencode/opencode.json` (user). JSON `mcp[name] = entry`.
16
+ * - github-copilot → `.vscode/mcp.json` (project) or the
17
+ * VS Code user `mcp.json`. JSON `servers[name] = entry`.
15
18
  * - codex → `$CODEX_HOME/config.toml` when set,
16
19
  * otherwise `~/.codex/config.toml`.
17
20
  * `[mcp_servers.<name>]` block.
@@ -19,7 +22,7 @@
19
22
  * Node-only. No new npm deps — hand-rolled JSON merge + minimal TOML block
20
23
  * merge, mirroring `mcp.ts`.
21
24
  */
22
- export type ClientId = "claude-code" | "claude-code-cli" | "codex" | "cowork";
25
+ export type ClientId = "claude-code" | "claude-code-cli" | "codex" | "cowork" | "cursor" | "opencode" | "github-copilot";
23
26
  export declare const CLIENTS: ClientId[];
24
27
  /** The HTTP MCP server entry written into a JSON client config. */
25
28
  export interface HttpMcpEntry {
@@ -29,6 +32,8 @@ export interface HttpMcpEntry {
29
32
  }
30
33
  /** Build the HTTP MCP server entry for a deployed agent-native app. */
31
34
  export declare function buildHttpMcpEntry(mcpUrl: string, token?: string, headers?: Record<string, string>): HttpMcpEntry;
35
+ export declare function buildHttpMcpEntryForClient(client: ClientId, mcpUrl: string, token?: string, headers?: Record<string, string>): Record<string, unknown>;
36
+ export declare function buildLocalMcpEntryForClient(client: ClientId, args: string[], env?: Record<string, string>): Record<string, unknown>;
32
37
  /**
33
38
  * Cowork consumes MCP exactly like Claude Code (same JSON server-entry
34
39
  * shape). Resolved lazily so `os.homedir()` reflects the current `$HOME`.
@@ -37,6 +42,12 @@ export declare function coworkConfigPath(): string;
37
42
  export declare function claudeCodeProjectConfig(baseDir: string): string;
38
43
  export declare function claudeCodeUserConfig(): string;
39
44
  export declare function codexConfigPath(): string;
45
+ export declare function cursorProjectConfig(baseDir: string): string;
46
+ export declare function cursorUserConfig(): string;
47
+ export declare function opencodeProjectConfig(baseDir: string): string;
48
+ export declare function opencodeUserConfig(): string;
49
+ export declare function githubCopilotProjectConfig(baseDir: string): string;
50
+ export declare function githubCopilotUserConfig(): string;
40
51
  /**
41
52
  * Resolve the on-disk config path for a client.
42
53
  *
@@ -60,10 +71,14 @@ export declare function writeFileAtomic(file: string, data: string): void;
60
71
  * Pass `entry === null` to delete the named entry. Re-running with the same
61
72
  * name replaces the existing entry in place — never duplicates.
62
73
  */
74
+ export declare function jsonMcpConfigKeyForClient(client: ClientId): string;
63
75
  export declare function writeJsonMcpEntry(file: string, name: string, entry: Record<string, unknown> | null): void;
76
+ export declare function writeJsonMcpEntryForClient(client: ClientId, file: string, name: string, entry: Record<string, unknown> | null): void;
64
77
  export declare function hasJsonMcpEntry(file: string, name: string): boolean;
78
+ export declare function hasJsonMcpEntryForClient(client: ClientId, file: string, name: string): boolean;
65
79
  /** Build a `[mcp_servers.<name>]` block for an HTTP-type MCP server. */
66
80
  export declare function buildCodexHttpBlock(name: string, mcpUrl: string, token?: string, headers?: Record<string, string>): string;
81
+ export declare function buildCodexLocalBlock(name: string, args: string[], env?: Record<string, string>): string;
67
82
  /**
68
83
  * Replace (or append) the `[mcp_servers.<name>]` block in a TOML file
69
84
  * without disturbing other content. A block is the header line plus every
@@ -84,14 +99,6 @@ export declare function writeHttpEntryForClient(client: ClientId, serverName: st
84
99
  * slashes. Returns `undefined` for invalid URLs.
85
100
  */
86
101
  export declare function canonicalUrl(value: string | undefined): string | undefined;
87
- /**
88
- * After writing the canonical `keepName` entry into a JSON config file,
89
- * remove any OTHER entries whose URL normalises to the same value as
90
- * `mcpUrl`. This cleans up stale alias names, legacy default names, and
91
- * leftover custom names that all pointed at the same server.
92
- *
93
- * Returns the list of entry names that were removed.
94
- */
95
102
  export declare function removeJsonSameUrlDuplicates(file: string, mcpUrl: string, keepName: string): string[];
96
103
  /**
97
104
  * After writing the canonical `keepName` Codex block, remove any OTHER
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-config-writers.d.ts","sourceRoot":"","sources":["../../src/cli/mcp-config-writers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mcp-config-writers.d.ts","sourceRoot":"","sources":["../../src/cli/mcp-config-writers.ts"],"names":[],"mappings":"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"}
@@ -3,15 +3,18 @@
3
3
  *
4
4
  * Extracted so both `agent-native mcp install` (see `mcp.ts`) and
5
5
  * `agent-native connect` (see `connect.ts`) write the EXACT same on-disk
6
- * config file targets and formats for every supported client. `mcp.ts`
7
- * intentionally keeps its own hand-rolled copies of these writers (its
8
- * external behavior is unchanged); new code should import from here so the
9
- * formats never diverge.
6
+ * config file targets and formats for every supported client.
10
7
  *
11
8
  * Supported clients and their config files:
12
9
  * - claude-code / claude-code-cli → `.mcp.json` (project) or
13
10
  * `~/.claude.json` (user). JSON `mcpServers[name] = entry`.
14
11
  * - cowork → `~/.cowork/mcp.json`. Same JSON shape.
12
+ * - cursor → `.cursor/mcp.json` (project) or
13
+ * `~/.cursor/mcp.json` (user). JSON `mcpServers[name] = entry`.
14
+ * - opencode → `opencode.json` (project) or
15
+ * `~/.config/opencode/opencode.json` (user). JSON `mcp[name] = entry`.
16
+ * - github-copilot → `.vscode/mcp.json` (project) or the
17
+ * VS Code user `mcp.json`. JSON `servers[name] = entry`.
15
18
  * - codex → `$CODEX_HOME/config.toml` when set,
16
19
  * otherwise `~/.codex/config.toml`.
17
20
  * `[mcp_servers.<name>]` block.
@@ -27,6 +30,9 @@ export const CLIENTS = [
27
30
  "claude-code-cli",
28
31
  "codex",
29
32
  "cowork",
33
+ "cursor",
34
+ "opencode",
35
+ "github-copilot",
30
36
  ];
31
37
  /** Build the HTTP MCP server entry for a deployed agent-native app. */
32
38
  export function buildHttpMcpEntry(mcpUrl, token, headers) {
@@ -40,6 +46,63 @@ export function buildHttpMcpEntry(mcpUrl, token, headers) {
40
46
  ...(Object.keys(mergedHeaders).length ? { headers: mergedHeaders } : {}),
41
47
  };
42
48
  }
49
+ function mergedHeadersFor(token, headers) {
50
+ return {
51
+ ...(headers ?? {}),
52
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
53
+ };
54
+ }
55
+ export function buildHttpMcpEntryForClient(client, mcpUrl, token, headers) {
56
+ const mergedHeaders = mergedHeadersFor(token, headers);
57
+ if (client === "cursor") {
58
+ return {
59
+ url: mcpUrl,
60
+ ...(Object.keys(mergedHeaders).length ? { headers: mergedHeaders } : {}),
61
+ };
62
+ }
63
+ if (client === "opencode") {
64
+ return {
65
+ type: "remote",
66
+ url: mcpUrl,
67
+ enabled: true,
68
+ ...(Object.keys(mergedHeaders).length ? { headers: mergedHeaders } : {}),
69
+ };
70
+ }
71
+ if (client === "github-copilot") {
72
+ return {
73
+ type: "http",
74
+ url: mcpUrl,
75
+ ...(Object.keys(mergedHeaders).length
76
+ ? { requestInit: { headers: mergedHeaders } }
77
+ : {}),
78
+ };
79
+ }
80
+ return buildHttpMcpEntry(mcpUrl, token, headers);
81
+ }
82
+ export function buildLocalMcpEntryForClient(client, args, env) {
83
+ const cleanEnv = env ? Object.fromEntries(Object.entries(env)) : {};
84
+ if (client === "opencode") {
85
+ return {
86
+ type: "local",
87
+ command: ["agent-native", ...args],
88
+ enabled: true,
89
+ ...(Object.keys(cleanEnv).length ? { environment: cleanEnv } : {}),
90
+ };
91
+ }
92
+ if (client === "github-copilot") {
93
+ return {
94
+ type: "stdio",
95
+ command: "agent-native",
96
+ args,
97
+ ...(Object.keys(cleanEnv).length ? { env: cleanEnv } : {}),
98
+ };
99
+ }
100
+ return {
101
+ command: "agent-native",
102
+ args,
103
+ ...(Object.keys(cleanEnv).length ? { env: cleanEnv } : {}),
104
+ };
105
+ }
43
106
  // ---------------------------------------------------------------------------
44
107
  // Config file locations — kept identical to `mcp.ts`.
45
108
  // ---------------------------------------------------------------------------
@@ -62,6 +125,35 @@ export function codexConfigPath() {
62
125
  return path.join(codexHome, "config.toml");
63
126
  return path.join(os.homedir(), ".codex", "config.toml");
64
127
  }
128
+ export function cursorProjectConfig(baseDir) {
129
+ return path.join(baseDir, ".cursor", "mcp.json");
130
+ }
131
+ export function cursorUserConfig() {
132
+ return path.join(os.homedir(), ".cursor", "mcp.json");
133
+ }
134
+ export function opencodeProjectConfig(baseDir) {
135
+ return path.join(baseDir, "opencode.json");
136
+ }
137
+ export function opencodeUserConfig() {
138
+ const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim();
139
+ const configRoot = xdgConfigHome || path.join(os.homedir(), ".config");
140
+ return path.join(configRoot, "opencode", "opencode.json");
141
+ }
142
+ export function githubCopilotProjectConfig(baseDir) {
143
+ return path.join(baseDir, ".vscode", "mcp.json");
144
+ }
145
+ export function githubCopilotUserConfig() {
146
+ if (process.platform === "darwin") {
147
+ return path.join(os.homedir(), "Library", "Application Support", "Code", "User", "mcp.json");
148
+ }
149
+ if (process.platform === "win32") {
150
+ const appData = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
151
+ return path.join(appData, "Code", "User", "mcp.json");
152
+ }
153
+ const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim();
154
+ const configRoot = xdgConfigHome || path.join(os.homedir(), ".config");
155
+ return path.join(configRoot, "Code", "User", "mcp.json");
156
+ }
65
157
  /**
66
158
  * Resolve the on-disk config path for a client.
67
159
  *
@@ -80,6 +172,18 @@ export function configPathFor(client, baseDir, scope) {
80
172
  return coworkConfigPath();
81
173
  case "codex":
82
174
  return codexConfigPath();
175
+ case "cursor":
176
+ return scope === "user"
177
+ ? cursorUserConfig()
178
+ : cursorProjectConfig(baseDir);
179
+ case "opencode":
180
+ return scope === "user"
181
+ ? opencodeUserConfig()
182
+ : opencodeProjectConfig(baseDir);
183
+ case "github-copilot":
184
+ return scope === "user"
185
+ ? githubCopilotUserConfig()
186
+ : githubCopilotProjectConfig(baseDir);
83
187
  }
84
188
  }
85
189
  // ---------------------------------------------------------------------------
@@ -156,23 +260,42 @@ export function writeFileAtomic(file, data) {
156
260
  * Pass `entry === null` to delete the named entry. Re-running with the same
157
261
  * name replaces the existing entry in place — never duplicates.
158
262
  */
159
- export function writeJsonMcpEntry(file, name, entry) {
263
+ export function jsonMcpConfigKeyForClient(client) {
264
+ if (client === "opencode")
265
+ return "mcp";
266
+ if (client === "github-copilot")
267
+ return "servers";
268
+ return "mcpServers";
269
+ }
270
+ function writeJsonMcpEntryAtKey(file, serversKey, name, entry) {
160
271
  const config = readJsonFile(file);
161
- if (!config.mcpServers || typeof config.mcpServers !== "object") {
162
- config.mcpServers = {};
272
+ if (!config[serversKey] || typeof config[serversKey] !== "object") {
273
+ config[serversKey] = {};
163
274
  }
275
+ const servers = config[serversKey];
164
276
  if (entry === null) {
165
- delete config.mcpServers[name];
277
+ delete servers[name];
166
278
  }
167
279
  else {
168
- config.mcpServers[name] = entry;
280
+ servers[name] = entry;
169
281
  }
170
282
  writeFileAtomic(file, JSON.stringify(config, null, 2) + "\n");
171
283
  }
284
+ export function writeJsonMcpEntry(file, name, entry) {
285
+ writeJsonMcpEntryAtKey(file, "mcpServers", name, entry);
286
+ }
287
+ export function writeJsonMcpEntryForClient(client, file, name, entry) {
288
+ writeJsonMcpEntryAtKey(file, jsonMcpConfigKeyForClient(client), name, entry);
289
+ }
172
290
  export function hasJsonMcpEntry(file, name) {
173
291
  const config = readJsonFile(file);
174
292
  return !!config?.mcpServers && name in config.mcpServers;
175
293
  }
294
+ export function hasJsonMcpEntryForClient(client, file, name) {
295
+ const config = readJsonFile(file);
296
+ const servers = config?.[jsonMcpConfigKeyForClient(client)];
297
+ return !!servers && typeof servers === "object" && name in servers;
298
+ }
176
299
  // ---------------------------------------------------------------------------
177
300
  // Codex TOML (hand-rolled minimal block merge, no new dep)
178
301
  // ---------------------------------------------------------------------------
@@ -201,6 +324,19 @@ export function buildCodexHttpBlock(name, mcpUrl, token, headers) {
201
324
  }
202
325
  return lines.join("\n") + "\n";
203
326
  }
327
+ export function buildCodexLocalBlock(name, args, env) {
328
+ const lines = [codexMcpHeader(name)];
329
+ lines.push(`command = "agent-native"`);
330
+ lines.push(`args = [${args.map(tomlQuote).join(", ")}]`);
331
+ const cleanEnv = env ? Object.fromEntries(Object.entries(env)) : {};
332
+ if (Object.keys(cleanEnv).length) {
333
+ const inline = Object.entries(cleanEnv)
334
+ .map(([key, value]) => `${key} = ${tomlQuote(value)}`)
335
+ .join(", ");
336
+ lines.push(`env = { ${inline} }`);
337
+ }
338
+ return lines.join("\n") + "\n";
339
+ }
204
340
  /**
205
341
  * Replace (or append) the `[mcp_servers.<name>]` block in a TOML file
206
342
  * without disturbing other content. A block is the header line plus every
@@ -272,7 +408,7 @@ export function writeHttpEntryForClient(client, serverName, mcpUrl, token, baseD
272
408
  writeCodexBlock(file, serverName, buildCodexHttpBlock(serverName, mcpUrl, token, headers));
273
409
  }
274
410
  else {
275
- writeJsonMcpEntry(file, serverName, buildHttpMcpEntry(mcpUrl, token, headers));
411
+ writeJsonMcpEntryForClient(client, file, serverName, buildHttpMcpEntryForClient(client, mcpUrl, token, headers));
276
412
  }
277
413
  return file;
278
414
  }
@@ -304,7 +440,7 @@ export function canonicalUrl(value) {
304
440
  *
305
441
  * Returns the list of entry names that were removed.
306
442
  */
307
- export function removeJsonSameUrlDuplicates(file, mcpUrl, keepName) {
443
+ function removeJsonSameUrlDuplicatesAtKey(file, serversKey, mcpUrl, keepName) {
308
444
  let config;
309
445
  try {
310
446
  const raw = fs.readFileSync(file, "utf-8");
@@ -315,7 +451,7 @@ export function removeJsonSameUrlDuplicates(file, mcpUrl, keepName) {
315
451
  catch {
316
452
  return [];
317
453
  }
318
- const servers = config?.mcpServers;
454
+ const servers = config?.[serversKey];
319
455
  if (!servers || typeof servers !== "object" || Array.isArray(servers)) {
320
456
  return [];
321
457
  }
@@ -342,6 +478,9 @@ export function removeJsonSameUrlDuplicates(file, mcpUrl, keepName) {
342
478
  writeFileAtomic(file, JSON.stringify(config, null, 2) + "\n");
343
479
  return toRemove;
344
480
  }
481
+ export function removeJsonSameUrlDuplicates(file, mcpUrl, keepName) {
482
+ return removeJsonSameUrlDuplicatesAtKey(file, "mcpServers", mcpUrl, keepName);
483
+ }
345
484
  /**
346
485
  * After writing the canonical `keepName` Codex block, remove any OTHER
347
486
  * `[mcp_servers.*]` blocks in the same TOML file whose `url =` line
@@ -417,6 +556,6 @@ export function removeSameUrlDuplicatesForClient(client, serverName, mcpUrl, bas
417
556
  if (client === "codex") {
418
557
  return removeCodexSameUrlDuplicates(file, mcpUrl, serverName);
419
558
  }
420
- return removeJsonSameUrlDuplicates(file, mcpUrl, serverName);
559
+ return removeJsonSameUrlDuplicatesAtKey(file, jsonMcpConfigKeyForClient(client), mcpUrl, serverName);
421
560
  }
422
561
  //# sourceMappingURL=mcp-config-writers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-config-writers.js","sourceRoot":"","sources":["../../src/cli/mcp-config-writers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared MCP client-config writers.\n *\n * Extracted so both `agent-native mcp install` (see `mcp.ts`) and\n * `agent-native connect` (see `connect.ts`) write the EXACT same on-disk\n * config file targets and formats for every supported client. `mcp.ts`\n * intentionally keeps its own hand-rolled copies of these writers (its\n * external behavior is unchanged); new code should import from here so the\n * formats never diverge.\n *\n * Supported clients and their config files:\n * - claude-code / claude-code-cli → `.mcp.json` (project) or\n * `~/.claude.json` (user). JSON `mcpServers[name] = entry`.\n * - cowork → `~/.cowork/mcp.json`. Same JSON shape.\n * - codex → `$CODEX_HOME/config.toml` when set,\n * otherwise `~/.codex/config.toml`.\n * `[mcp_servers.<name>]` block.\n *\n * Node-only. No new npm deps — hand-rolled JSON merge + minimal TOML block\n * merge, mirroring `mcp.ts`.\n */\n\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nexport type ClientId = \"claude-code\" | \"claude-code-cli\" | \"codex\" | \"cowork\";\n\nexport const CLIENTS: ClientId[] = [\n \"claude-code\",\n \"claude-code-cli\",\n \"codex\",\n \"cowork\",\n];\n\n/** The HTTP MCP server entry written into a JSON client config. */\nexport interface HttpMcpEntry {\n type: \"http\";\n url: string;\n headers?: Record<string, string>;\n}\n\n/** Build the HTTP MCP server entry for a deployed agent-native app. */\nexport function buildHttpMcpEntry(\n mcpUrl: string,\n token?: string,\n headers?: Record<string, string>,\n): HttpMcpEntry {\n const mergedHeaders = {\n ...(headers ?? {}),\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n };\n return {\n type: \"http\",\n url: mcpUrl,\n ...(Object.keys(mergedHeaders).length ? { headers: mergedHeaders } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Config file locations — kept identical to `mcp.ts`.\n// ---------------------------------------------------------------------------\n\n/**\n * Cowork consumes MCP exactly like Claude Code (same JSON server-entry\n * shape). Resolved lazily so `os.homedir()` reflects the current `$HOME`.\n */\nexport function coworkConfigPath(): string {\n return path.join(os.homedir(), \".cowork\", \"mcp.json\");\n}\n\nexport function claudeCodeProjectConfig(baseDir: string): string {\n return path.join(baseDir, \".mcp.json\");\n}\n\nexport function claudeCodeUserConfig(): string {\n return path.join(os.homedir(), \".claude.json\");\n}\n\nexport function codexConfigPath(): string {\n const codexHome = process.env.CODEX_HOME?.trim();\n if (codexHome) return path.join(codexHome, \"config.toml\");\n return path.join(os.homedir(), \".codex\", \"config.toml\");\n}\n\n/**\n * Resolve the on-disk config path for a client.\n *\n * `scope` only affects Claude Code / Claude Code CLI: `\"user\"` → the global\n * `~/.claude.json`, anything else → the project-local `.mcp.json` rooted at\n * `baseDir`.\n */\nexport function configPathFor(\n client: ClientId,\n baseDir: string,\n scope: string | undefined,\n): string {\n switch (client) {\n case \"claude-code\":\n case \"claude-code-cli\":\n return scope === \"user\"\n ? claudeCodeUserConfig()\n : claudeCodeProjectConfig(baseDir);\n case \"cowork\":\n return coworkConfigPath();\n case \"codex\":\n return codexConfigPath();\n }\n}\n\n// ---------------------------------------------------------------------------\n// JSON client configs (Claude Code, Claude Code CLI, Cowork)\n// ---------------------------------------------------------------------------\n\n/**\n * Read and parse a JSON config file.\n *\n * - Missing file → returns `{}` (fresh config).\n * - Empty file → returns `{}` (treat as not-yet-initialised).\n * - Non-empty file that fails to parse → throws a descriptive Error so the\n * caller can surface it to the user instead of silently overwriting the\n * file with only the new MCP entry (data-loss hazard).\n */\nfunction readJsonFile(file: string): Record<string, any> {\n let raw: string;\n try {\n raw = fs.readFileSync(file, \"utf-8\");\n } catch {\n // Missing (ENOENT) or unreadable file — treat as empty.\n return {};\n }\n if (!raw.trim()) return {};\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === \"object\" ? parsed : {};\n } catch {\n throw new Error(\n `Cannot parse JSON config file: ${file}\\n` +\n `Fix or move the file and re-run. The file has not been modified.`,\n );\n }\n}\n\n/**\n * Write `data` to `file` atomically: write a sibling temp file, then rename it\n * over the target. `rename(2)` is atomic on the same filesystem, so a crash or\n * `kill` mid-write can never leave a half-written/truncated file. This matters\n * most for `~/.claude.json`, which is Claude Code's entire user state (projects,\n * history, auth) — a torn write there would corrupt the user's whole config,\n * not just our MCP entry. The temp file lives in the target's directory so the\n * rename stays within one filesystem.\n */\nexport function writeFileAtomic(file: string, data: string): void {\n const dir = path.dirname(file);\n fs.mkdirSync(dir, { recursive: true });\n // Preserve the target's existing permission bits. A fresh temp file would\n // otherwise be created with the umask default (typically 0644), silently\n // loosening a secret-bearing file the user locked down to 0600 (e.g. .env).\n let mode: number | undefined;\n try {\n mode = fs.statSync(file).mode & 0o777;\n } catch {\n // Target doesn't exist yet — let the default creation mode apply.\n }\n const tmp = path.join(dir, `.${path.basename(file)}.tmp-${process.pid}`);\n try {\n fs.writeFileSync(tmp, data, \"utf-8\");\n if (mode !== undefined) fs.chmodSync(tmp, mode);\n fs.renameSync(tmp, file);\n } catch (err) {\n try {\n fs.rmSync(tmp, { force: true });\n } catch {}\n throw err;\n }\n}\n\n/**\n * Idempotently write `mcpServers[name] = entry` into a JSON config file.\n * Pass `entry === null` to delete the named entry. Re-running with the same\n * name replaces the existing entry in place — never duplicates.\n */\nexport function writeJsonMcpEntry(\n file: string,\n name: string,\n entry: Record<string, unknown> | null,\n): void {\n const config = readJsonFile(file);\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n config.mcpServers = {};\n }\n if (entry === null) {\n delete config.mcpServers[name];\n } else {\n config.mcpServers[name] = entry;\n }\n writeFileAtomic(file, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\nexport function hasJsonMcpEntry(file: string, name: string): boolean {\n const config = readJsonFile(file);\n return !!config?.mcpServers && name in config.mcpServers;\n}\n\n// ---------------------------------------------------------------------------\n// Codex TOML (hand-rolled minimal block merge, no new dep)\n// ---------------------------------------------------------------------------\n\nfunction tomlQuote(s: string): string {\n return `\"${s.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n}\n\nfunction codexMcpHeader(name: string): string {\n return `[mcp_servers.${tomlQuote(name)}]`;\n}\n\nfunction legacyCodexMcpHeader(name: string): string | null {\n return /^[A-Za-z0-9_-]+$/.test(name) ? `[mcp_servers.${name}]` : null;\n}\n\n/** Build a `[mcp_servers.<name>]` block for an HTTP-type MCP server. */\nexport function buildCodexHttpBlock(\n name: string,\n mcpUrl: string,\n token?: string,\n headers?: Record<string, string>,\n): string {\n const lines: string[] = [codexMcpHeader(name)];\n lines.push(`url = ${tomlQuote(mcpUrl)}`);\n const mergedHeaders = {\n ...(headers ?? {}),\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n };\n const headerEntries = Object.entries(mergedHeaders);\n if (headerEntries.length) {\n lines.push(\n `http_headers = { ${headerEntries\n .map(([key, value]) => `${tomlQuote(key)} = ${tomlQuote(value)}`)\n .join(\", \")} }`,\n );\n }\n return lines.join(\"\\n\") + \"\\n\";\n}\n\n/**\n * Replace (or append) the `[mcp_servers.<name>]` block in a TOML file\n * without disturbing other content. A block is the header line plus every\n * following line until the next top-level `[` table header or EOF. Pass\n * `block === null` to remove the block. Identical algorithm to `mcp.ts`'s\n * `writeCodexBlock` so the two never diverge.\n */\nexport function writeCodexBlock(\n file: string,\n name: string,\n block: string | null,\n): void {\n let content = \"\";\n try {\n content = fs.readFileSync(file, \"utf-8\");\n } catch {\n content = \"\";\n }\n\n const headers = new Set(\n [codexMcpHeader(name), legacyCodexMcpHeader(name)].filter(\n Boolean,\n ) as string[],\n );\n const lines = content.split(/\\r?\\n/);\n const out: string[] = [];\n let i = 0;\n let removed = false;\n while (i < lines.length) {\n const line = lines[i];\n if (headers.has(line.trim())) {\n // Skip this block entirely (header + body until next table header).\n removed = true;\n i++;\n while (i < lines.length && !/^\\s*\\[/.test(lines[i])) i++;\n continue;\n }\n out.push(line);\n i++;\n }\n\n let next = out\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .replace(/\\n*$/, \"\\n\");\n if (block !== null) {\n next = next.replace(/\\n*$/, \"\\n\");\n if (next.trim().length) next += \"\\n\";\n next += block;\n }\n if (block === null && !removed) return; // nothing to do\n\n writeFileAtomic(file, next);\n}\n\nexport function codexHasBlock(file: string, name: string): boolean {\n try {\n const content = fs.readFileSync(file, \"utf-8\");\n const headers = new Set(\n [codexMcpHeader(name), legacyCodexMcpHeader(name)].filter(\n Boolean,\n ) as string[],\n );\n return content.split(/\\r?\\n/).some((line) => headers.has(line.trim()));\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Unified write helper\n// ---------------------------------------------------------------------------\n\n/**\n * Idempotently write the HTTP MCP server entry for `serverName` into the\n * given client's config file and return the file path that was written.\n * Re-running replaces the same named entry — never duplicates.\n */\nexport function writeHttpEntryForClient(\n client: ClientId,\n serverName: string,\n mcpUrl: string,\n token: string | undefined,\n baseDir: string,\n scope: string | undefined,\n headers?: Record<string, string>,\n): string {\n const file = configPathFor(client, baseDir, scope);\n if (client === \"codex\") {\n writeCodexBlock(\n file,\n serverName,\n buildCodexHttpBlock(serverName, mcpUrl, token, headers),\n );\n } else {\n writeJsonMcpEntry(\n file,\n serverName,\n buildHttpMcpEntry(mcpUrl, token, headers) as unknown as Record<\n string,\n unknown\n >,\n );\n }\n return file;\n}\n\n// ---------------------------------------------------------------------------\n// Same-URL duplicate removal\n// ---------------------------------------------------------------------------\n\n/**\n * Canonicalise a URL for comparison: strip hash, search params, and trailing\n * slashes. Returns `undefined` for invalid URLs.\n */\nexport function canonicalUrl(value: string | undefined): string | undefined {\n if (!value) return undefined;\n try {\n const u = new URL(value);\n u.hash = \"\";\n u.search = \"\";\n return u.toString().replace(/\\/+$/, \"\");\n } catch {\n return undefined;\n }\n}\n\n/**\n * After writing the canonical `keepName` entry into a JSON config file,\n * remove any OTHER entries whose URL normalises to the same value as\n * `mcpUrl`. This cleans up stale alias names, legacy default names, and\n * leftover custom names that all pointed at the same server.\n *\n * Returns the list of entry names that were removed.\n */\nexport function removeJsonSameUrlDuplicates(\n file: string,\n mcpUrl: string,\n keepName: string,\n): string[] {\n let config: Record<string, any>;\n try {\n const raw = fs.readFileSync(file, \"utf-8\");\n if (!raw.trim()) return [];\n config = JSON.parse(raw);\n } catch {\n return [];\n }\n const servers = config?.mcpServers;\n if (!servers || typeof servers !== \"object\" || Array.isArray(servers)) {\n return [];\n }\n const targetCanonical = canonicalUrl(mcpUrl);\n if (!targetCanonical) return [];\n\n const toRemove: string[] = [];\n for (const name of Object.keys(servers)) {\n if (name === keepName) continue;\n const entry = servers[name];\n if (!entry || typeof entry !== \"object\") continue;\n const entryUrl = typeof entry.url === \"string\" ? entry.url : undefined;\n if (canonicalUrl(entryUrl) === targetCanonical) {\n toRemove.push(name);\n }\n }\n if (toRemove.length === 0) return [];\n for (const name of toRemove) {\n delete servers[name];\n }\n writeFileAtomic(file, JSON.stringify(config, null, 2) + \"\\n\");\n return toRemove;\n}\n\n/**\n * After writing the canonical `keepName` Codex block, remove any OTHER\n * `[mcp_servers.*]` blocks in the same TOML file whose `url =` line\n * normalises to the same value as `mcpUrl`. Returns removed entry names.\n */\nexport function removeCodexSameUrlDuplicates(\n file: string,\n mcpUrl: string,\n keepName: string,\n): string[] {\n let content = \"\";\n try {\n content = fs.readFileSync(file, \"utf-8\");\n } catch {\n return [];\n }\n const targetCanonical = canonicalUrl(mcpUrl);\n if (!targetCanonical) return [];\n\n const lines = content.split(/\\r?\\n/);\n const out: string[] = [];\n const removed: string[] = [];\n let i = 0;\n while (i < lines.length) {\n const line = lines[i];\n const trimmed = line.trim();\n const quoted = trimmed.match(/^\\[mcp_servers\\.\"((?:\\\\.|[^\"])*)\"\\]$/);\n const bare = trimmed.match(/^\\[mcp_servers\\.([A-Za-z0-9_-]+)\\]$/);\n const serverName = quoted\n ? quoted[1].replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\")\n : bare?.[1];\n if (serverName !== undefined && serverName !== keepName) {\n // Collect the block\n const block: string[] = [line];\n i++;\n while (i < lines.length && !/^\\s*\\[/.test(lines[i])) {\n block.push(lines[i]);\n i++;\n }\n // Check url in block\n const urlMatch = block\n .join(\"\\n\")\n .match(/^\\s*url\\s*=\\s*\"((?:\\\\.|[^\"])*)\"/m);\n const blockUrl = urlMatch\n ? urlMatch[1].replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\")\n : undefined;\n if (canonicalUrl(blockUrl) === targetCanonical) {\n removed.push(serverName);\n // Skip this block (don't push to out)\n continue;\n }\n // Not a duplicate — keep it\n for (const l of block) out.push(l);\n continue;\n }\n out.push(line);\n i++;\n }\n\n if (removed.length === 0) return [];\n const next = out\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .replace(/\\n*$/, \"\\n\");\n writeFileAtomic(file, next);\n return removed;\n}\n\n/**\n * Unified helper: after writing the canonical `serverName` entry for the\n * given `client`, remove same-URL duplicates from its config file.\n * Returns the list of removed names (empty if nothing was cleaned up).\n */\nexport function removeSameUrlDuplicatesForClient(\n client: ClientId,\n serverName: string,\n mcpUrl: string,\n baseDir: string,\n scope: string | undefined,\n): string[] {\n const file = configPathFor(client, baseDir, scope);\n if (client === \"codex\") {\n return removeCodexSameUrlDuplicates(file, mcpUrl, serverName);\n }\n return removeJsonSameUrlDuplicates(file, mcpUrl, serverName);\n}\n"]}
1
+ {"version":3,"file":"mcp-config-writers.js","sourceRoot":"","sources":["../../src/cli/mcp-config-writers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared MCP client-config writers.\n *\n * Extracted so both `agent-native mcp install` (see `mcp.ts`) and\n * `agent-native connect` (see `connect.ts`) write the EXACT same on-disk\n * config file targets and formats for every supported client.\n *\n * Supported clients and their config files:\n * - claude-code / claude-code-cli → `.mcp.json` (project) or\n * `~/.claude.json` (user). JSON `mcpServers[name] = entry`.\n * - cowork → `~/.cowork/mcp.json`. Same JSON shape.\n * - cursor → `.cursor/mcp.json` (project) or\n * `~/.cursor/mcp.json` (user). JSON `mcpServers[name] = entry`.\n * - opencode → `opencode.json` (project) or\n * `~/.config/opencode/opencode.json` (user). JSON `mcp[name] = entry`.\n * - github-copilot → `.vscode/mcp.json` (project) or the\n * VS Code user `mcp.json`. JSON `servers[name] = entry`.\n * - codex → `$CODEX_HOME/config.toml` when set,\n * otherwise `~/.codex/config.toml`.\n * `[mcp_servers.<name>]` block.\n *\n * Node-only. No new npm deps — hand-rolled JSON merge + minimal TOML block\n * merge, mirroring `mcp.ts`.\n */\n\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nexport type ClientId =\n | \"claude-code\"\n | \"claude-code-cli\"\n | \"codex\"\n | \"cowork\"\n | \"cursor\"\n | \"opencode\"\n | \"github-copilot\";\n\nexport const CLIENTS: ClientId[] = [\n \"claude-code\",\n \"claude-code-cli\",\n \"codex\",\n \"cowork\",\n \"cursor\",\n \"opencode\",\n \"github-copilot\",\n];\n\n/** The HTTP MCP server entry written into a JSON client config. */\nexport interface HttpMcpEntry {\n type: \"http\";\n url: string;\n headers?: Record<string, string>;\n}\n\n/** Build the HTTP MCP server entry for a deployed agent-native app. */\nexport function buildHttpMcpEntry(\n mcpUrl: string,\n token?: string,\n headers?: Record<string, string>,\n): HttpMcpEntry {\n const mergedHeaders = {\n ...(headers ?? {}),\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n };\n return {\n type: \"http\",\n url: mcpUrl,\n ...(Object.keys(mergedHeaders).length ? { headers: mergedHeaders } : {}),\n };\n}\n\nfunction mergedHeadersFor(\n token?: string,\n headers?: Record<string, string>,\n): Record<string, string> {\n return {\n ...(headers ?? {}),\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n };\n}\n\nexport function buildHttpMcpEntryForClient(\n client: ClientId,\n mcpUrl: string,\n token?: string,\n headers?: Record<string, string>,\n): Record<string, unknown> {\n const mergedHeaders = mergedHeadersFor(token, headers);\n if (client === \"cursor\") {\n return {\n url: mcpUrl,\n ...(Object.keys(mergedHeaders).length ? { headers: mergedHeaders } : {}),\n };\n }\n if (client === \"opencode\") {\n return {\n type: \"remote\",\n url: mcpUrl,\n enabled: true,\n ...(Object.keys(mergedHeaders).length ? { headers: mergedHeaders } : {}),\n };\n }\n if (client === \"github-copilot\") {\n return {\n type: \"http\",\n url: mcpUrl,\n ...(Object.keys(mergedHeaders).length\n ? { requestInit: { headers: mergedHeaders } }\n : {}),\n };\n }\n return buildHttpMcpEntry(mcpUrl, token, headers) as unknown as Record<\n string,\n unknown\n >;\n}\n\nexport function buildLocalMcpEntryForClient(\n client: ClientId,\n args: string[],\n env?: Record<string, string>,\n): Record<string, unknown> {\n const cleanEnv = env ? Object.fromEntries(Object.entries(env)) : {};\n if (client === \"opencode\") {\n return {\n type: \"local\",\n command: [\"agent-native\", ...args],\n enabled: true,\n ...(Object.keys(cleanEnv).length ? { environment: cleanEnv } : {}),\n };\n }\n if (client === \"github-copilot\") {\n return {\n type: \"stdio\",\n command: \"agent-native\",\n args,\n ...(Object.keys(cleanEnv).length ? { env: cleanEnv } : {}),\n };\n }\n return {\n command: \"agent-native\",\n args,\n ...(Object.keys(cleanEnv).length ? { env: cleanEnv } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Config file locations — kept identical to `mcp.ts`.\n// ---------------------------------------------------------------------------\n\n/**\n * Cowork consumes MCP exactly like Claude Code (same JSON server-entry\n * shape). Resolved lazily so `os.homedir()` reflects the current `$HOME`.\n */\nexport function coworkConfigPath(): string {\n return path.join(os.homedir(), \".cowork\", \"mcp.json\");\n}\n\nexport function claudeCodeProjectConfig(baseDir: string): string {\n return path.join(baseDir, \".mcp.json\");\n}\n\nexport function claudeCodeUserConfig(): string {\n return path.join(os.homedir(), \".claude.json\");\n}\n\nexport function codexConfigPath(): string {\n const codexHome = process.env.CODEX_HOME?.trim();\n if (codexHome) return path.join(codexHome, \"config.toml\");\n return path.join(os.homedir(), \".codex\", \"config.toml\");\n}\n\nexport function cursorProjectConfig(baseDir: string): string {\n return path.join(baseDir, \".cursor\", \"mcp.json\");\n}\n\nexport function cursorUserConfig(): string {\n return path.join(os.homedir(), \".cursor\", \"mcp.json\");\n}\n\nexport function opencodeProjectConfig(baseDir: string): string {\n return path.join(baseDir, \"opencode.json\");\n}\n\nexport function opencodeUserConfig(): string {\n const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim();\n const configRoot = xdgConfigHome || path.join(os.homedir(), \".config\");\n return path.join(configRoot, \"opencode\", \"opencode.json\");\n}\n\nexport function githubCopilotProjectConfig(baseDir: string): string {\n return path.join(baseDir, \".vscode\", \"mcp.json\");\n}\n\nexport function githubCopilotUserConfig(): string {\n if (process.platform === \"darwin\") {\n return path.join(\n os.homedir(),\n \"Library\",\n \"Application Support\",\n \"Code\",\n \"User\",\n \"mcp.json\",\n );\n }\n if (process.platform === \"win32\") {\n const appData =\n process.env.APPDATA || path.join(os.homedir(), \"AppData\", \"Roaming\");\n return path.join(appData, \"Code\", \"User\", \"mcp.json\");\n }\n const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim();\n const configRoot = xdgConfigHome || path.join(os.homedir(), \".config\");\n return path.join(configRoot, \"Code\", \"User\", \"mcp.json\");\n}\n\n/**\n * Resolve the on-disk config path for a client.\n *\n * `scope` only affects Claude Code / Claude Code CLI: `\"user\"` → the global\n * `~/.claude.json`, anything else → the project-local `.mcp.json` rooted at\n * `baseDir`.\n */\nexport function configPathFor(\n client: ClientId,\n baseDir: string,\n scope: string | undefined,\n): string {\n switch (client) {\n case \"claude-code\":\n case \"claude-code-cli\":\n return scope === \"user\"\n ? claudeCodeUserConfig()\n : claudeCodeProjectConfig(baseDir);\n case \"cowork\":\n return coworkConfigPath();\n case \"codex\":\n return codexConfigPath();\n case \"cursor\":\n return scope === \"user\"\n ? cursorUserConfig()\n : cursorProjectConfig(baseDir);\n case \"opencode\":\n return scope === \"user\"\n ? opencodeUserConfig()\n : opencodeProjectConfig(baseDir);\n case \"github-copilot\":\n return scope === \"user\"\n ? githubCopilotUserConfig()\n : githubCopilotProjectConfig(baseDir);\n }\n}\n\n// ---------------------------------------------------------------------------\n// JSON client configs (Claude Code, Claude Code CLI, Cowork)\n// ---------------------------------------------------------------------------\n\n/**\n * Read and parse a JSON config file.\n *\n * - Missing file → returns `{}` (fresh config).\n * - Empty file → returns `{}` (treat as not-yet-initialised).\n * - Non-empty file that fails to parse → throws a descriptive Error so the\n * caller can surface it to the user instead of silently overwriting the\n * file with only the new MCP entry (data-loss hazard).\n */\nfunction readJsonFile(file: string): Record<string, any> {\n let raw: string;\n try {\n raw = fs.readFileSync(file, \"utf-8\");\n } catch {\n // Missing (ENOENT) or unreadable file — treat as empty.\n return {};\n }\n if (!raw.trim()) return {};\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === \"object\" ? parsed : {};\n } catch {\n throw new Error(\n `Cannot parse JSON config file: ${file}\\n` +\n `Fix or move the file and re-run. The file has not been modified.`,\n );\n }\n}\n\n/**\n * Write `data` to `file` atomically: write a sibling temp file, then rename it\n * over the target. `rename(2)` is atomic on the same filesystem, so a crash or\n * `kill` mid-write can never leave a half-written/truncated file. This matters\n * most for `~/.claude.json`, which is Claude Code's entire user state (projects,\n * history, auth) — a torn write there would corrupt the user's whole config,\n * not just our MCP entry. The temp file lives in the target's directory so the\n * rename stays within one filesystem.\n */\nexport function writeFileAtomic(file: string, data: string): void {\n const dir = path.dirname(file);\n fs.mkdirSync(dir, { recursive: true });\n // Preserve the target's existing permission bits. A fresh temp file would\n // otherwise be created with the umask default (typically 0644), silently\n // loosening a secret-bearing file the user locked down to 0600 (e.g. .env).\n let mode: number | undefined;\n try {\n mode = fs.statSync(file).mode & 0o777;\n } catch {\n // Target doesn't exist yet — let the default creation mode apply.\n }\n const tmp = path.join(dir, `.${path.basename(file)}.tmp-${process.pid}`);\n try {\n fs.writeFileSync(tmp, data, \"utf-8\");\n if (mode !== undefined) fs.chmodSync(tmp, mode);\n fs.renameSync(tmp, file);\n } catch (err) {\n try {\n fs.rmSync(tmp, { force: true });\n } catch {}\n throw err;\n }\n}\n\n/**\n * Idempotently write `mcpServers[name] = entry` into a JSON config file.\n * Pass `entry === null` to delete the named entry. Re-running with the same\n * name replaces the existing entry in place — never duplicates.\n */\nexport function jsonMcpConfigKeyForClient(client: ClientId): string {\n if (client === \"opencode\") return \"mcp\";\n if (client === \"github-copilot\") return \"servers\";\n return \"mcpServers\";\n}\n\nfunction writeJsonMcpEntryAtKey(\n file: string,\n serversKey: string,\n name: string,\n entry: Record<string, unknown> | null,\n): void {\n const config = readJsonFile(file);\n if (!config[serversKey] || typeof config[serversKey] !== \"object\") {\n config[serversKey] = {};\n }\n const servers = config[serversKey] as Record<string, unknown>;\n if (entry === null) {\n delete servers[name];\n } else {\n servers[name] = entry;\n }\n writeFileAtomic(file, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\nexport function writeJsonMcpEntry(\n file: string,\n name: string,\n entry: Record<string, unknown> | null,\n): void {\n writeJsonMcpEntryAtKey(file, \"mcpServers\", name, entry);\n}\n\nexport function writeJsonMcpEntryForClient(\n client: ClientId,\n file: string,\n name: string,\n entry: Record<string, unknown> | null,\n): void {\n writeJsonMcpEntryAtKey(file, jsonMcpConfigKeyForClient(client), name, entry);\n}\n\nexport function hasJsonMcpEntry(file: string, name: string): boolean {\n const config = readJsonFile(file);\n return !!config?.mcpServers && name in config.mcpServers;\n}\n\nexport function hasJsonMcpEntryForClient(\n client: ClientId,\n file: string,\n name: string,\n): boolean {\n const config = readJsonFile(file);\n const servers = config?.[jsonMcpConfigKeyForClient(client)];\n return !!servers && typeof servers === \"object\" && name in servers;\n}\n\n// ---------------------------------------------------------------------------\n// Codex TOML (hand-rolled minimal block merge, no new dep)\n// ---------------------------------------------------------------------------\n\nfunction tomlQuote(s: string): string {\n return `\"${s.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n}\n\nfunction codexMcpHeader(name: string): string {\n return `[mcp_servers.${tomlQuote(name)}]`;\n}\n\nfunction legacyCodexMcpHeader(name: string): string | null {\n return /^[A-Za-z0-9_-]+$/.test(name) ? `[mcp_servers.${name}]` : null;\n}\n\n/** Build a `[mcp_servers.<name>]` block for an HTTP-type MCP server. */\nexport function buildCodexHttpBlock(\n name: string,\n mcpUrl: string,\n token?: string,\n headers?: Record<string, string>,\n): string {\n const lines: string[] = [codexMcpHeader(name)];\n lines.push(`url = ${tomlQuote(mcpUrl)}`);\n const mergedHeaders = {\n ...(headers ?? {}),\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n };\n const headerEntries = Object.entries(mergedHeaders);\n if (headerEntries.length) {\n lines.push(\n `http_headers = { ${headerEntries\n .map(([key, value]) => `${tomlQuote(key)} = ${tomlQuote(value)}`)\n .join(\", \")} }`,\n );\n }\n return lines.join(\"\\n\") + \"\\n\";\n}\n\nexport function buildCodexLocalBlock(\n name: string,\n args: string[],\n env?: Record<string, string>,\n): string {\n const lines: string[] = [codexMcpHeader(name)];\n lines.push(`command = \"agent-native\"`);\n lines.push(`args = [${args.map(tomlQuote).join(\", \")}]`);\n const cleanEnv = env ? Object.fromEntries(Object.entries(env)) : {};\n if (Object.keys(cleanEnv).length) {\n const inline = Object.entries(cleanEnv)\n .map(([key, value]) => `${key} = ${tomlQuote(value)}`)\n .join(\", \");\n lines.push(`env = { ${inline} }`);\n }\n return lines.join(\"\\n\") + \"\\n\";\n}\n\n/**\n * Replace (or append) the `[mcp_servers.<name>]` block in a TOML file\n * without disturbing other content. A block is the header line plus every\n * following line until the next top-level `[` table header or EOF. Pass\n * `block === null` to remove the block. Identical algorithm to `mcp.ts`'s\n * `writeCodexBlock` so the two never diverge.\n */\nexport function writeCodexBlock(\n file: string,\n name: string,\n block: string | null,\n): void {\n let content = \"\";\n try {\n content = fs.readFileSync(file, \"utf-8\");\n } catch {\n content = \"\";\n }\n\n const headers = new Set(\n [codexMcpHeader(name), legacyCodexMcpHeader(name)].filter(\n Boolean,\n ) as string[],\n );\n const lines = content.split(/\\r?\\n/);\n const out: string[] = [];\n let i = 0;\n let removed = false;\n while (i < lines.length) {\n const line = lines[i];\n if (headers.has(line.trim())) {\n // Skip this block entirely (header + body until next table header).\n removed = true;\n i++;\n while (i < lines.length && !/^\\s*\\[/.test(lines[i])) i++;\n continue;\n }\n out.push(line);\n i++;\n }\n\n let next = out\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .replace(/\\n*$/, \"\\n\");\n if (block !== null) {\n next = next.replace(/\\n*$/, \"\\n\");\n if (next.trim().length) next += \"\\n\";\n next += block;\n }\n if (block === null && !removed) return; // nothing to do\n\n writeFileAtomic(file, next);\n}\n\nexport function codexHasBlock(file: string, name: string): boolean {\n try {\n const content = fs.readFileSync(file, \"utf-8\");\n const headers = new Set(\n [codexMcpHeader(name), legacyCodexMcpHeader(name)].filter(\n Boolean,\n ) as string[],\n );\n return content.split(/\\r?\\n/).some((line) => headers.has(line.trim()));\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Unified write helper\n// ---------------------------------------------------------------------------\n\n/**\n * Idempotently write the HTTP MCP server entry for `serverName` into the\n * given client's config file and return the file path that was written.\n * Re-running replaces the same named entry — never duplicates.\n */\nexport function writeHttpEntryForClient(\n client: ClientId,\n serverName: string,\n mcpUrl: string,\n token: string | undefined,\n baseDir: string,\n scope: string | undefined,\n headers?: Record<string, string>,\n): string {\n const file = configPathFor(client, baseDir, scope);\n if (client === \"codex\") {\n writeCodexBlock(\n file,\n serverName,\n buildCodexHttpBlock(serverName, mcpUrl, token, headers),\n );\n } else {\n writeJsonMcpEntryForClient(\n client,\n file,\n serverName,\n buildHttpMcpEntryForClient(client, mcpUrl, token, headers),\n );\n }\n return file;\n}\n\n// ---------------------------------------------------------------------------\n// Same-URL duplicate removal\n// ---------------------------------------------------------------------------\n\n/**\n * Canonicalise a URL for comparison: strip hash, search params, and trailing\n * slashes. Returns `undefined` for invalid URLs.\n */\nexport function canonicalUrl(value: string | undefined): string | undefined {\n if (!value) return undefined;\n try {\n const u = new URL(value);\n u.hash = \"\";\n u.search = \"\";\n return u.toString().replace(/\\/+$/, \"\");\n } catch {\n return undefined;\n }\n}\n\n/**\n * After writing the canonical `keepName` entry into a JSON config file,\n * remove any OTHER entries whose URL normalises to the same value as\n * `mcpUrl`. This cleans up stale alias names, legacy default names, and\n * leftover custom names that all pointed at the same server.\n *\n * Returns the list of entry names that were removed.\n */\nfunction removeJsonSameUrlDuplicatesAtKey(\n file: string,\n serversKey: string,\n mcpUrl: string,\n keepName: string,\n): string[] {\n let config: Record<string, any>;\n try {\n const raw = fs.readFileSync(file, \"utf-8\");\n if (!raw.trim()) return [];\n config = JSON.parse(raw);\n } catch {\n return [];\n }\n const servers = config?.[serversKey];\n if (!servers || typeof servers !== \"object\" || Array.isArray(servers)) {\n return [];\n }\n const targetCanonical = canonicalUrl(mcpUrl);\n if (!targetCanonical) return [];\n\n const toRemove: string[] = [];\n for (const name of Object.keys(servers)) {\n if (name === keepName) continue;\n const entry = servers[name];\n if (!entry || typeof entry !== \"object\") continue;\n const entryUrl = typeof entry.url === \"string\" ? entry.url : undefined;\n if (canonicalUrl(entryUrl) === targetCanonical) {\n toRemove.push(name);\n }\n }\n if (toRemove.length === 0) return [];\n for (const name of toRemove) {\n delete servers[name];\n }\n writeFileAtomic(file, JSON.stringify(config, null, 2) + \"\\n\");\n return toRemove;\n}\n\nexport function removeJsonSameUrlDuplicates(\n file: string,\n mcpUrl: string,\n keepName: string,\n): string[] {\n return removeJsonSameUrlDuplicatesAtKey(file, \"mcpServers\", mcpUrl, keepName);\n}\n\n/**\n * After writing the canonical `keepName` Codex block, remove any OTHER\n * `[mcp_servers.*]` blocks in the same TOML file whose `url =` line\n * normalises to the same value as `mcpUrl`. Returns removed entry names.\n */\nexport function removeCodexSameUrlDuplicates(\n file: string,\n mcpUrl: string,\n keepName: string,\n): string[] {\n let content = \"\";\n try {\n content = fs.readFileSync(file, \"utf-8\");\n } catch {\n return [];\n }\n const targetCanonical = canonicalUrl(mcpUrl);\n if (!targetCanonical) return [];\n\n const lines = content.split(/\\r?\\n/);\n const out: string[] = [];\n const removed: string[] = [];\n let i = 0;\n while (i < lines.length) {\n const line = lines[i];\n const trimmed = line.trim();\n const quoted = trimmed.match(/^\\[mcp_servers\\.\"((?:\\\\.|[^\"])*)\"\\]$/);\n const bare = trimmed.match(/^\\[mcp_servers\\.([A-Za-z0-9_-]+)\\]$/);\n const serverName = quoted\n ? quoted[1].replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\")\n : bare?.[1];\n if (serverName !== undefined && serverName !== keepName) {\n // Collect the block\n const block: string[] = [line];\n i++;\n while (i < lines.length && !/^\\s*\\[/.test(lines[i])) {\n block.push(lines[i]);\n i++;\n }\n // Check url in block\n const urlMatch = block\n .join(\"\\n\")\n .match(/^\\s*url\\s*=\\s*\"((?:\\\\.|[^\"])*)\"/m);\n const blockUrl = urlMatch\n ? urlMatch[1].replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\")\n : undefined;\n if (canonicalUrl(blockUrl) === targetCanonical) {\n removed.push(serverName);\n // Skip this block (don't push to out)\n continue;\n }\n // Not a duplicate — keep it\n for (const l of block) out.push(l);\n continue;\n }\n out.push(line);\n i++;\n }\n\n if (removed.length === 0) return [];\n const next = out\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .replace(/\\n*$/, \"\\n\");\n writeFileAtomic(file, next);\n return removed;\n}\n\n/**\n * Unified helper: after writing the canonical `serverName` entry for the\n * given `client`, remove same-URL duplicates from its config file.\n * Returns the list of removed names (empty if nothing was cleaned up).\n */\nexport function removeSameUrlDuplicatesForClient(\n client: ClientId,\n serverName: string,\n mcpUrl: string,\n baseDir: string,\n scope: string | undefined,\n): string[] {\n const file = configPathFor(client, baseDir, scope);\n if (client === \"codex\") {\n return removeCodexSameUrlDuplicates(file, mcpUrl, serverName);\n }\n return removeJsonSameUrlDuplicatesAtKey(\n file,\n jsonMcpConfigKeyForClient(client),\n mcpUrl,\n serverName,\n );\n}\n"]}
package/dist/cli/mcp.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * `agent-native mcp <subcommand>` — connect external coding agents (Claude
3
- * Code desktop & CLI, Claude Cowork, Codex) to this agent-native app/workspace
4
- * over MCP.
3
+ * Code desktop & CLI, Claude Cowork, Codex, Cursor, OpenCode, GitHub Copilot /
4
+ * VS Code) to this agent-native app/workspace over MCP.
5
5
  *
6
6
  * serve Run the MCP stdio transport (this is what client configs spawn).
7
7
  * install Provision a token + write the client's MCP config idempotently.
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/cli/mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAypBH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+B1D"}
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/cli/mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA2gBH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+B1D"}