@agent-native/core 0.48.3 → 0.49.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 (148) hide show
  1. package/dist/agent/context-xray/actions/context-evict.d.ts +1 -1
  2. package/dist/agent/context-xray/actions/context-pin.d.ts +1 -1
  3. package/dist/agent/context-xray/actions/context-report.d.ts +4 -4
  4. package/dist/agent/context-xray/actions/context-restore.d.ts +1 -1
  5. package/dist/application-state/handlers.d.ts +2 -2
  6. package/dist/application-state/handlers.d.ts.map +1 -1
  7. package/dist/cli/app-skill.d.ts +157 -0
  8. package/dist/cli/app-skill.d.ts.map +1 -0
  9. package/dist/cli/app-skill.js +17 -7
  10. package/dist/cli/app-skill.js.map +1 -1
  11. package/dist/cli/audit-agent-web.d.ts +2 -0
  12. package/dist/cli/audit-agent-web.d.ts.map +1 -0
  13. package/dist/cli/code-agent-connector.d.ts +17 -0
  14. package/dist/cli/code-agent-connector.d.ts.map +1 -0
  15. package/dist/cli/code.d.ts +66 -0
  16. package/dist/cli/code.d.ts.map +1 -0
  17. package/dist/cli/connect.d.ts +168 -0
  18. package/dist/cli/connect.d.ts.map +1 -0
  19. package/dist/cli/connect.js +118 -30
  20. package/dist/cli/connect.js.map +1 -1
  21. package/dist/cli/context-xray-local.d.ts +16 -0
  22. package/dist/cli/context-xray-local.d.ts.map +1 -0
  23. package/dist/cli/create-workspace.d.ts +8 -0
  24. package/dist/cli/create-workspace.d.ts.map +1 -0
  25. package/dist/cli/index.d.ts +3 -0
  26. package/dist/cli/index.d.ts.map +1 -0
  27. package/dist/cli/info.d.ts +2 -0
  28. package/dist/cli/info.d.ts.map +1 -0
  29. package/dist/cli/mcp-config-writers.d.ts +108 -0
  30. package/dist/cli/mcp-config-writers.d.ts.map +1 -0
  31. package/dist/cli/mcp-config-writers.js +143 -0
  32. package/dist/cli/mcp-config-writers.js.map +1 -1
  33. package/dist/cli/mcp.d.ts +16 -0
  34. package/dist/cli/mcp.d.ts.map +1 -0
  35. package/dist/cli/mcp.js +10 -10
  36. package/dist/cli/mcp.js.map +1 -1
  37. package/dist/cli/migrate.d.ts +38 -0
  38. package/dist/cli/migrate.d.ts.map +1 -0
  39. package/dist/cli/plan-local.d.ts +43 -0
  40. package/dist/cli/plan-local.d.ts.map +1 -0
  41. package/dist/cli/plan-publish-store.d.ts +62 -0
  42. package/dist/cli/plan-publish-store.d.ts.map +1 -0
  43. package/dist/cli/pr-visual-recap-workflow.d.ts +11 -0
  44. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -0
  45. package/dist/cli/pr-visual-recap-workflow.js +1 -1
  46. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  47. package/dist/cli/recap.d.ts +453 -0
  48. package/dist/cli/recap.d.ts.map +1 -0
  49. package/dist/cli/recap.js +228 -95
  50. package/dist/cli/recap.js.map +1 -1
  51. package/dist/cli/skills.d.ts +193 -0
  52. package/dist/cli/skills.d.ts.map +1 -0
  53. package/dist/cli/skills.js +518 -177
  54. package/dist/cli/skills.js.map +1 -1
  55. package/dist/cli/telemetry.d.ts +13 -0
  56. package/dist/cli/telemetry.d.ts.map +1 -0
  57. package/dist/cli/telemetry.js +115 -0
  58. package/dist/cli/telemetry.js.map +1 -0
  59. package/dist/cli/workspace-dev.d.ts +96 -0
  60. package/dist/cli/workspace-dev.d.ts.map +1 -0
  61. package/dist/client/AssistantChat.d.ts.map +1 -1
  62. package/dist/client/AssistantChat.js +10 -19
  63. package/dist/client/AssistantChat.js.map +1 -1
  64. package/dist/client/ErrorBoundary.d.ts.map +1 -1
  65. package/dist/client/ErrorBoundary.js +34 -1
  66. package/dist/client/ErrorBoundary.js.map +1 -1
  67. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
  68. package/dist/client/blocks/library/AnnotatedCodeBlock.js +15 -7
  69. package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
  70. package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
  71. package/dist/client/blocks/library/DiffBlock.js +17 -10
  72. package/dist/client/blocks/library/DiffBlock.js.map +1 -1
  73. package/dist/client/blocks/library/annotation-rail.d.ts +5 -0
  74. package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
  75. package/dist/client/blocks/library/annotation-rail.js +6 -0
  76. package/dist/client/blocks/library/annotation-rail.js.map +1 -1
  77. package/dist/client/blocks/types.d.ts +5 -0
  78. package/dist/client/blocks/types.d.ts.map +1 -1
  79. package/dist/client/blocks/types.js.map +1 -1
  80. package/dist/client/index.d.ts +1 -1
  81. package/dist/client/index.d.ts.map +1 -1
  82. package/dist/client/index.js +1 -1
  83. package/dist/client/index.js.map +1 -1
  84. package/dist/client/route-chunk-recovery.d.ts +17 -0
  85. package/dist/client/route-chunk-recovery.d.ts.map +1 -1
  86. package/dist/client/route-chunk-recovery.js +67 -0
  87. package/dist/client/route-chunk-recovery.js.map +1 -1
  88. package/dist/deploy/build.d.ts.map +1 -1
  89. package/dist/deploy/build.js +15 -71
  90. package/dist/deploy/build.js.map +1 -1
  91. package/dist/extensions/schema.d.ts +54 -54
  92. package/dist/extensions/slots/schema.d.ts +13 -13
  93. package/dist/file-upload/actions/upload-image.d.ts +4 -4
  94. package/dist/mcp/actions/create-org-service-token.d.ts +1 -1
  95. package/dist/mcp/actions/list-org-service-tokens.d.ts +7 -7
  96. package/dist/mcp/build-server.d.ts +12 -12
  97. package/dist/mcp/build-server.d.ts.map +1 -1
  98. package/dist/mcp/build-server.js.map +1 -1
  99. package/dist/mcp/connect-route.js +1 -1
  100. package/dist/mcp/connect-route.js.map +1 -1
  101. package/dist/mcp/oauth-route.d.ts +10 -0
  102. package/dist/mcp/oauth-route.d.ts.map +1 -1
  103. package/dist/mcp/oauth-route.js +34 -3
  104. package/dist/mcp/oauth-route.js.map +1 -1
  105. package/dist/mcp/oauth-store.d.ts +15 -1
  106. package/dist/mcp/oauth-store.d.ts.map +1 -1
  107. package/dist/mcp/oauth-store.js +60 -4
  108. package/dist/mcp/oauth-store.js.map +1 -1
  109. package/dist/mcp/oauth-token.d.ts +3 -1
  110. package/dist/mcp/oauth-token.d.ts.map +1 -1
  111. package/dist/mcp/oauth-token.js +78 -6
  112. package/dist/mcp/oauth-token.js.map +1 -1
  113. package/dist/mcp/server.d.ts.map +1 -1
  114. package/dist/mcp/server.js +8 -6
  115. package/dist/mcp/server.js.map +1 -1
  116. package/dist/observability/routes.d.ts +11 -11
  117. package/dist/org/handlers.d.ts +7 -11
  118. package/dist/org/handlers.d.ts.map +1 -1
  119. package/dist/secrets/schema.d.ts +7 -7
  120. package/dist/server/auth.d.ts.map +1 -1
  121. package/dist/server/auth.js +8 -5
  122. package/dist/server/auth.js.map +1 -1
  123. package/dist/server/csrf.d.ts +1 -1
  124. package/dist/server/csrf.d.ts.map +1 -1
  125. package/dist/server/onboarding-html.d.ts.map +1 -1
  126. package/dist/server/onboarding-html.js +12 -11
  127. package/dist/server/onboarding-html.js.map +1 -1
  128. package/dist/server/poll-events.d.ts +1 -1
  129. package/dist/server/security-headers.d.ts +1 -1
  130. package/dist/server/security-headers.d.ts.map +1 -1
  131. package/dist/server/ssr-handler.d.ts.map +1 -1
  132. package/dist/server/ssr-handler.js +42 -130
  133. package/dist/server/ssr-handler.js.map +1 -1
  134. package/dist/sharing/actions/list-resource-shares.d.ts +3 -3
  135. package/dist/sharing/actions/set-resource-visibility.d.ts +2 -2
  136. package/dist/sharing/actions/share-resource.d.ts +4 -4
  137. package/dist/sharing/actions/unshare-resource.d.ts +1 -1
  138. package/dist/sharing/schema.d.ts +12 -12
  139. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +2 -2
  140. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +6 -6
  141. package/dist/templates/workspace-core/.agents/skills/external-agents/references/mcp-apps-embedding.md +2 -2
  142. package/dist/workspace-files/schema.d.ts +8 -8
  143. package/docs/content/external-agents.md +14 -0
  144. package/docs/content/plan-plugin.md +16 -7
  145. package/package.json +5 -1
  146. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +2 -2
  147. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +6 -6
  148. package/src/templates/workspace-core/.agents/skills/external-agents/references/mcp-apps-embedding.md +2 -2
@@ -0,0 +1,16 @@
1
+ import type { ClientId } from "./mcp-config-writers.js";
2
+ export interface InstallLocalContextXrayOptions {
3
+ baseDir?: string;
4
+ clients: ClientId[];
5
+ scope: string;
6
+ dryRun?: boolean;
7
+ }
8
+ export interface InstallLocalContextXrayResult {
9
+ commands: string[];
10
+ scriptPath: string;
11
+ written: string[];
12
+ }
13
+ export declare const CONTEXT_XRAY_SKILL_MD = "---\nname: context-xray\ndescription: >-\n Visualize local Codex and Claude Code context usage, open an inline/browser\n report, flag warnings, and suggest prompt/tooling optimizations. Use when the\n user types /context-xray, asks where context is going, wants recent local\n coding-agent trends, or wants to improve context efficiency.\nmetadata:\n visibility: exported\n---\n\n# Context X-Ray\n\nUse the locally installed Context X-Ray command to visualize recent Codex and\nClaude Code context usage. It reads local transcript files only and does not\nupload transcript content.\n\nProject-scoped installs write only project `.agents` skill and command\nartifacts; user-scoped installs write global Codex/Claude instructions.\n\n## Run\n\nCurrent or most recent local thread:\n\n```sh\n~/.agent-native/context-xray/context-xray --open\n```\n\nThread picker / recent sessions:\n\n```sh\n~/.agent-native/context-xray/context-xray threads --open\n```\n\nWeekly trends:\n\n```sh\n~/.agent-native/context-xray/context-xray trends --since 7d --open\n```\n\nExact session when the host exposes one:\n\n```sh\n~/.agent-native/context-xray/context-xray --session-id \"$CLAUDE_CODE_SESSION_ID\" --open\n```\n\nAfter running, report the link, the number of sessions analyzed, the largest\ncontext buckets, and 3-5 specific optimizations.\n`--open` opens the generated local HTML file directly and does not keep a\nbackground report server running.\n\n## Interpret\n\n- Treat the Overview score as a triage signal, then open Findings for evidence.\n- Repeated file reads: ask the agent to keep a short file-role note and reopen\n only when exact line numbers are needed.\n- Retry loops or failed tool loops: ask the agent to stop after two identical\n failures, summarize the error, and change strategy before rerunning.\n- Exploration heavy: give an inspection budget, then ask for a short\n implementation plan before more reading.\n- Cache churn or context growth: move stable instructions into skills or repo\n docs and continue large work from a compact handoff summary.\n- Tool output heavy: ask the agent to cap command output, summarize failures,\n and only expand logs when exact lines matter.\n- Metadata heavy: use Metadata/Sources drilldowns for protocol overhead, but\n prioritize prompt changes around user/tool/output buckets first.\n";
14
+ export declare const CONTEXT_XRAY_COMMAND_MD = "---\ndescription: Visualize local Codex/Claude context usage and get optimization tips.\nargument-hint: [current|threads|trends|--since 7d]\n---\n\nRun Context X-Ray locally and show the user the generated report link plus the\ntop warnings.\n\nChoose the command from the user's arguments:\n\n- No arguments or `current`:\n `~/.agent-native/context-xray/context-xray --open`\n- `threads`:\n `~/.agent-native/context-xray/context-xray threads --open`\n- `trends`:\n `~/.agent-native/context-xray/context-xray trends --since 7d --open`\n\nIf `$ARGUMENTS` includes flags such as `--since 24h`, `--last 20`, or\n`--all-projects`, pass them through to the command. If the host exposes\n`CLAUDE_CODE_SESSION_ID`, prefer:\n\n```sh\n~/.agent-native/context-xray/context-xray --session-id \"$CLAUDE_CODE_SESSION_ID\" --open\n```\n\n`--open` opens a local HTML report file directly; there should not be a\nlong-running server process to monitor.\n\nAfter the command finishes, summarize:\n\n- the report link\n- sessions analyzed\n- the health score and most important finding\n- one concrete evidence point\n- two or three promptable ways to improve this thread\n";
15
+ export declare function installLocalContextXray(options: InstallLocalContextXrayOptions): InstallLocalContextXrayResult;
16
+ //# sourceMappingURL=context-xray-local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-xray-local.d.ts","sourceRoot":"","sources":["../../src/cli/context-xray-local.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAExD,MAAM,WAAW,8BAA8B;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAk7DD,eAAO,MAAM,qBAAqB,qzEAkEjC,CAAC;AAEF,eAAO,MAAM,uBAAuB,2oCAmCnC,CAAC;AA+BF,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,8BAA8B,GACtC,6BAA6B,CAqE/B"}
@@ -0,0 +1,8 @@
1
+ export interface CreateWorkspaceOptions {
2
+ name?: string;
3
+ /** Pre-select these templates in the picker. */
4
+ template?: string;
5
+ noInstall?: boolean;
6
+ }
7
+ export declare function createWorkspace(opts?: CreateWorkspaceOptions): Promise<void>;
8
+ //# sourceMappingURL=create-workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-workspace.d.ts","sourceRoot":"","sources":["../../src/cli/create-workspace.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAsB,eAAe,CACnC,IAAI,GAAE,sBAA2B,GAChC,OAAO,CAAC,IAAI,CAAC,CAMf"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export declare function runInfo(pkgName?: string): void;
2
+ //# sourceMappingURL=info.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"info.d.ts","sourceRoot":"","sources":["../../src/cli/info.ts"],"names":[],"mappings":"AAUA,wBAAgB,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAmE9C"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Shared MCP client-config writers.
3
+ *
4
+ * Extracted so both `agent-native mcp install` (see `mcp.ts`) and
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.
10
+ *
11
+ * Supported clients and their config files:
12
+ * - claude-code / claude-code-cli → `.mcp.json` (project) or
13
+ * `~/.claude.json` (user). JSON `mcpServers[name] = entry`.
14
+ * - cowork → `~/.cowork/mcp.json`. Same JSON shape.
15
+ * - codex → `$CODEX_HOME/config.toml` when set,
16
+ * otherwise `~/.codex/config.toml`.
17
+ * `[mcp_servers.<name>]` block.
18
+ *
19
+ * Node-only. No new npm deps — hand-rolled JSON merge + minimal TOML block
20
+ * merge, mirroring `mcp.ts`.
21
+ */
22
+ export type ClientId = "claude-code" | "claude-code-cli" | "codex" | "cowork";
23
+ export declare const CLIENTS: ClientId[];
24
+ /** The HTTP MCP server entry written into a JSON client config. */
25
+ export interface HttpMcpEntry {
26
+ type: "http";
27
+ url: string;
28
+ headers?: Record<string, string>;
29
+ }
30
+ /** Build the HTTP MCP server entry for a deployed agent-native app. */
31
+ export declare function buildHttpMcpEntry(mcpUrl: string, token?: string, headers?: Record<string, string>): HttpMcpEntry;
32
+ /**
33
+ * Cowork consumes MCP exactly like Claude Code (same JSON server-entry
34
+ * shape). Resolved lazily so `os.homedir()` reflects the current `$HOME`.
35
+ */
36
+ export declare function coworkConfigPath(): string;
37
+ export declare function claudeCodeProjectConfig(baseDir: string): string;
38
+ export declare function claudeCodeUserConfig(): string;
39
+ export declare function codexConfigPath(): string;
40
+ /**
41
+ * Resolve the on-disk config path for a client.
42
+ *
43
+ * `scope` only affects Claude Code / Claude Code CLI: `"user"` → the global
44
+ * `~/.claude.json`, anything else → the project-local `.mcp.json` rooted at
45
+ * `baseDir`.
46
+ */
47
+ export declare function configPathFor(client: ClientId, baseDir: string, scope: string | undefined): string;
48
+ /**
49
+ * Write `data` to `file` atomically: write a sibling temp file, then rename it
50
+ * over the target. `rename(2)` is atomic on the same filesystem, so a crash or
51
+ * `kill` mid-write can never leave a half-written/truncated file. This matters
52
+ * most for `~/.claude.json`, which is Claude Code's entire user state (projects,
53
+ * history, auth) — a torn write there would corrupt the user's whole config,
54
+ * not just our MCP entry. The temp file lives in the target's directory so the
55
+ * rename stays within one filesystem.
56
+ */
57
+ export declare function writeFileAtomic(file: string, data: string): void;
58
+ /**
59
+ * Idempotently write `mcpServers[name] = entry` into a JSON config file.
60
+ * Pass `entry === null` to delete the named entry. Re-running with the same
61
+ * name replaces the existing entry in place — never duplicates.
62
+ */
63
+ export declare function writeJsonMcpEntry(file: string, name: string, entry: Record<string, unknown> | null): void;
64
+ export declare function hasJsonMcpEntry(file: string, name: string): boolean;
65
+ /** Build a `[mcp_servers.<name>]` block for an HTTP-type MCP server. */
66
+ export declare function buildCodexHttpBlock(name: string, mcpUrl: string, token?: string, headers?: Record<string, string>): string;
67
+ /**
68
+ * Replace (or append) the `[mcp_servers.<name>]` block in a TOML file
69
+ * without disturbing other content. A block is the header line plus every
70
+ * following line until the next top-level `[` table header or EOF. Pass
71
+ * `block === null` to remove the block. Identical algorithm to `mcp.ts`'s
72
+ * `writeCodexBlock` so the two never diverge.
73
+ */
74
+ export declare function writeCodexBlock(file: string, name: string, block: string | null): void;
75
+ export declare function codexHasBlock(file: string, name: string): boolean;
76
+ /**
77
+ * Idempotently write the HTTP MCP server entry for `serverName` into the
78
+ * given client's config file and return the file path that was written.
79
+ * Re-running replaces the same named entry — never duplicates.
80
+ */
81
+ export declare function writeHttpEntryForClient(client: ClientId, serverName: string, mcpUrl: string, token: string | undefined, baseDir: string, scope: string | undefined, headers?: Record<string, string>): string;
82
+ /**
83
+ * Canonicalise a URL for comparison: strip hash, search params, and trailing
84
+ * slashes. Returns `undefined` for invalid URLs.
85
+ */
86
+ 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
+ export declare function removeJsonSameUrlDuplicates(file: string, mcpUrl: string, keepName: string): string[];
96
+ /**
97
+ * After writing the canonical `keepName` Codex block, remove any OTHER
98
+ * `[mcp_servers.*]` blocks in the same TOML file whose `url =` line
99
+ * normalises to the same value as `mcpUrl`. Returns removed entry names.
100
+ */
101
+ export declare function removeCodexSameUrlDuplicates(file: string, mcpUrl: string, keepName: string): string[];
102
+ /**
103
+ * Unified helper: after writing the canonical `serverName` entry for the
104
+ * given `client`, remove same-URL duplicates from its config file.
105
+ * Returns the list of removed names (empty if nothing was cleaned up).
106
+ */
107
+ export declare function removeSameUrlDuplicatesForClient(client: ClientId, serverName: string, mcpUrl: string, baseDir: string, scope: string | undefined): string[];
108
+ //# sourceMappingURL=mcp-config-writers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-config-writers.d.ts","sourceRoot":"","sources":["../../src/cli/mcp-config-writers.ts"],"names":[],"mappings":"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"}
@@ -276,4 +276,147 @@ export function writeHttpEntryForClient(client, serverName, mcpUrl, token, baseD
276
276
  }
277
277
  return file;
278
278
  }
279
+ // ---------------------------------------------------------------------------
280
+ // Same-URL duplicate removal
281
+ // ---------------------------------------------------------------------------
282
+ /**
283
+ * Canonicalise a URL for comparison: strip hash, search params, and trailing
284
+ * slashes. Returns `undefined` for invalid URLs.
285
+ */
286
+ export function canonicalUrl(value) {
287
+ if (!value)
288
+ return undefined;
289
+ try {
290
+ const u = new URL(value);
291
+ u.hash = "";
292
+ u.search = "";
293
+ return u.toString().replace(/\/+$/, "");
294
+ }
295
+ catch {
296
+ return undefined;
297
+ }
298
+ }
299
+ /**
300
+ * After writing the canonical `keepName` entry into a JSON config file,
301
+ * remove any OTHER entries whose URL normalises to the same value as
302
+ * `mcpUrl`. This cleans up stale alias names, legacy default names, and
303
+ * leftover custom names that all pointed at the same server.
304
+ *
305
+ * Returns the list of entry names that were removed.
306
+ */
307
+ export function removeJsonSameUrlDuplicates(file, mcpUrl, keepName) {
308
+ let config;
309
+ try {
310
+ const raw = fs.readFileSync(file, "utf-8");
311
+ if (!raw.trim())
312
+ return [];
313
+ config = JSON.parse(raw);
314
+ }
315
+ catch {
316
+ return [];
317
+ }
318
+ const servers = config?.mcpServers;
319
+ if (!servers || typeof servers !== "object" || Array.isArray(servers)) {
320
+ return [];
321
+ }
322
+ const targetCanonical = canonicalUrl(mcpUrl);
323
+ if (!targetCanonical)
324
+ return [];
325
+ const toRemove = [];
326
+ for (const name of Object.keys(servers)) {
327
+ if (name === keepName)
328
+ continue;
329
+ const entry = servers[name];
330
+ if (!entry || typeof entry !== "object")
331
+ continue;
332
+ const entryUrl = typeof entry.url === "string" ? entry.url : undefined;
333
+ if (canonicalUrl(entryUrl) === targetCanonical) {
334
+ toRemove.push(name);
335
+ }
336
+ }
337
+ if (toRemove.length === 0)
338
+ return [];
339
+ for (const name of toRemove) {
340
+ delete servers[name];
341
+ }
342
+ writeFileAtomic(file, JSON.stringify(config, null, 2) + "\n");
343
+ return toRemove;
344
+ }
345
+ /**
346
+ * After writing the canonical `keepName` Codex block, remove any OTHER
347
+ * `[mcp_servers.*]` blocks in the same TOML file whose `url =` line
348
+ * normalises to the same value as `mcpUrl`. Returns removed entry names.
349
+ */
350
+ export function removeCodexSameUrlDuplicates(file, mcpUrl, keepName) {
351
+ let content = "";
352
+ try {
353
+ content = fs.readFileSync(file, "utf-8");
354
+ }
355
+ catch {
356
+ return [];
357
+ }
358
+ const targetCanonical = canonicalUrl(mcpUrl);
359
+ if (!targetCanonical)
360
+ return [];
361
+ const lines = content.split(/\r?\n/);
362
+ const out = [];
363
+ const removed = [];
364
+ let i = 0;
365
+ while (i < lines.length) {
366
+ const line = lines[i];
367
+ const trimmed = line.trim();
368
+ const quoted = trimmed.match(/^\[mcp_servers\."((?:\\.|[^"])*)"\]$/);
369
+ const bare = trimmed.match(/^\[mcp_servers\.([A-Za-z0-9_-]+)\]$/);
370
+ const serverName = quoted
371
+ ? quoted[1].replace(/\\"/g, '"').replace(/\\\\/g, "\\")
372
+ : bare?.[1];
373
+ if (serverName !== undefined && serverName !== keepName) {
374
+ // Collect the block
375
+ const block = [line];
376
+ i++;
377
+ while (i < lines.length && !/^\s*\[/.test(lines[i])) {
378
+ block.push(lines[i]);
379
+ i++;
380
+ }
381
+ // Check url in block
382
+ const urlMatch = block
383
+ .join("\n")
384
+ .match(/^\s*url\s*=\s*"((?:\\.|[^"])*)"/m);
385
+ const blockUrl = urlMatch
386
+ ? urlMatch[1].replace(/\\"/g, '"').replace(/\\\\/g, "\\")
387
+ : undefined;
388
+ if (canonicalUrl(blockUrl) === targetCanonical) {
389
+ removed.push(serverName);
390
+ // Skip this block (don't push to out)
391
+ continue;
392
+ }
393
+ // Not a duplicate — keep it
394
+ for (const l of block)
395
+ out.push(l);
396
+ continue;
397
+ }
398
+ out.push(line);
399
+ i++;
400
+ }
401
+ if (removed.length === 0)
402
+ return [];
403
+ const next = out
404
+ .join("\n")
405
+ .replace(/\n{3,}/g, "\n\n")
406
+ .replace(/\n*$/, "\n");
407
+ writeFileAtomic(file, next);
408
+ return removed;
409
+ }
410
+ /**
411
+ * Unified helper: after writing the canonical `serverName` entry for the
412
+ * given `client`, remove same-URL duplicates from its config file.
413
+ * Returns the list of removed names (empty if nothing was cleaned up).
414
+ */
415
+ export function removeSameUrlDuplicatesForClient(client, serverName, mcpUrl, baseDir, scope) {
416
+ const file = configPathFor(client, baseDir, scope);
417
+ if (client === "codex") {
418
+ return removeCodexSameUrlDuplicates(file, mcpUrl, serverName);
419
+ }
420
+ return removeJsonSameUrlDuplicates(file, mcpUrl, serverName);
421
+ }
279
422
  //# 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"]}
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"]}
@@ -0,0 +1,16 @@
1
+ /**
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.
5
+ *
6
+ * serve Run the MCP stdio transport (this is what client configs spawn).
7
+ * install Provision a token + write the client's MCP config idempotently.
8
+ * uninstall Remove the named entry from a client's MCP config.
9
+ * status Print resolved MCP URL/port, token state, and per-client entries.
10
+ * token Print or rotate the local ACCESS_TOKEN in the workspace .env.
11
+ *
12
+ * Node-only CLI module. Hand-rolled `.env` upsert + minimal TOML block merge
13
+ * keep this dependency-free (no new npm deps).
14
+ */
15
+ export declare function runMcp(args: string[]): Promise<void>;
16
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +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"}
package/dist/cli/mcp.js CHANGED
@@ -436,7 +436,7 @@ async function cmdServe(p) {
436
436
  async function cmdInstall(p) {
437
437
  const client = (p.client ?? "").toLowerCase();
438
438
  if (!CLIENTS.includes(client)) {
439
- logErr(`Usage: agent-native mcp install --client ${CLIENTS.join("|")} ` +
439
+ logErr(`Usage: npx @agent-native/core@latest mcp install --client ${CLIENTS.join("|")} ` +
440
440
  `[--app <id>] [--scope user|project]`);
441
441
  process.exit(1);
442
442
  }
@@ -479,13 +479,13 @@ async function cmdInstall(p) {
479
479
  logOut(`Installed "${serverName}" for ${client} → ${file}`);
480
480
  logOut(hostedUrl
481
481
  ? ` Mode: http (${hostedUrl})`
482
- : ` Mode: stdio (agent-native mcp serve --app ${appId}${p.standalone ? " --standalone" : ""})`);
482
+ : ` Mode: stdio (npx @agent-native/core@latest mcp serve --app ${appId}${p.standalone ? " --standalone" : ""})`);
483
483
  logOut(` Restart ${client} to pick up the new MCP server.`);
484
484
  }
485
485
  function cmdUninstall(p) {
486
486
  const client = (p.client ?? "").toLowerCase();
487
487
  if (!CLIENTS.includes(client)) {
488
- logErr(`Usage: agent-native mcp uninstall --client ${CLIENTS.join("|")} ` +
488
+ logErr(`Usage: npx @agent-native/core@latest mcp uninstall --client ${CLIENTS.join("|")} ` +
489
489
  `[--app <id>]`);
490
490
  process.exit(1);
491
491
  }
@@ -541,28 +541,28 @@ function cmdToken(p) {
541
541
  : `ACCESS_TOKEN (${t.file}):`);
542
542
  logOut(t.token);
543
543
  if (p.rotate) {
544
- logOut(` Re-run \`agent-native mcp install --client <c>\` so client configs ` +
544
+ logOut(` Re-run \`npx @agent-native/core@latest mcp install --client <c>\` so client configs ` +
545
545
  `pick up the new token.`);
546
546
  }
547
547
  }
548
- const HELP = `agent-native mcp — connect external coding agents over MCP
548
+ const HELP = `npx @agent-native/core@latest mcp — connect external coding agents over MCP
549
549
 
550
550
  Usage:
551
- agent-native mcp serve [--app <id>] [--port <n>] [--standalone]
551
+ npx @agent-native/core@latest mcp serve [--app <id>] [--port <n>] [--standalone]
552
552
  Run the MCP stdio transport (what client configs spawn).
553
553
  Default: proxy to the running local app; --standalone builds from disk.
554
554
 
555
- agent-native mcp install --client <c> [--app <id>] [--scope user|project]
555
+ npx @agent-native/core@latest mcp install --client <c> [--app <id>] [--scope user|project]
556
556
  Provision a token and write the client's MCP config (idempotent).
557
557
  Clients: claude-code, claude-code-cli, codex, cowork
558
558
 
559
- agent-native mcp uninstall --client <c> [--app <id>]
559
+ npx @agent-native/core@latest mcp uninstall --client <c> [--app <id>]
560
560
  Remove the named MCP entry from a client's config (idempotent).
561
561
 
562
- agent-native mcp status
562
+ npx @agent-native/core@latest mcp status
563
563
  Show resolved MCP URL/port, token state, and per-client entries.
564
564
 
565
- agent-native mcp token [--rotate]
565
+ npx @agent-native/core@latest mcp token [--rotate]
566
566
  Print (or rotate) the local ACCESS_TOKEN in the workspace .env.`;
567
567
  export async function runMcp(args) {
568
568
  const p = parseArgs(args);