@agent-native/core 0.51.15 → 0.53.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 (251) hide show
  1. package/README.md +42 -96
  2. package/blueprints/action/crud.md +98 -0
  3. package/blueprints/channel/discord.md +74 -0
  4. package/blueprints/provider/stripe.md +87 -0
  5. package/blueprints/sandbox/docker.md +78 -0
  6. package/dist/action.d.ts +24 -0
  7. package/dist/action.d.ts.map +1 -1
  8. package/dist/action.js +4 -0
  9. package/dist/action.js.map +1 -1
  10. package/dist/agent/observational-memory/compactor.d.ts +43 -0
  11. package/dist/agent/observational-memory/compactor.d.ts.map +1 -0
  12. package/dist/agent/observational-memory/compactor.js +50 -0
  13. package/dist/agent/observational-memory/compactor.js.map +1 -0
  14. package/dist/agent/observational-memory/config.d.ts +37 -0
  15. package/dist/agent/observational-memory/config.d.ts.map +1 -0
  16. package/dist/agent/observational-memory/config.js +48 -0
  17. package/dist/agent/observational-memory/config.js.map +1 -0
  18. package/dist/agent/observational-memory/index.d.ts +26 -0
  19. package/dist/agent/observational-memory/index.d.ts.map +1 -0
  20. package/dist/agent/observational-memory/index.js +25 -0
  21. package/dist/agent/observational-memory/index.js.map +1 -0
  22. package/dist/agent/observational-memory/internal-run.d.ts +37 -0
  23. package/dist/agent/observational-memory/internal-run.d.ts.map +1 -0
  24. package/dist/agent/observational-memory/internal-run.js +59 -0
  25. package/dist/agent/observational-memory/internal-run.js.map +1 -0
  26. package/dist/agent/observational-memory/message-text.d.ts +13 -0
  27. package/dist/agent/observational-memory/message-text.d.ts.map +1 -0
  28. package/dist/agent/observational-memory/message-text.js +46 -0
  29. package/dist/agent/observational-memory/message-text.js.map +1 -0
  30. package/dist/agent/observational-memory/migrations.d.ts +13 -0
  31. package/dist/agent/observational-memory/migrations.d.ts.map +1 -0
  32. package/dist/agent/observational-memory/migrations.js +43 -0
  33. package/dist/agent/observational-memory/migrations.js.map +1 -0
  34. package/dist/agent/observational-memory/observer.d.ts +37 -0
  35. package/dist/agent/observational-memory/observer.d.ts.map +1 -0
  36. package/dist/agent/observational-memory/observer.js +82 -0
  37. package/dist/agent/observational-memory/observer.js.map +1 -0
  38. package/dist/agent/observational-memory/plugin.d.ts +16 -0
  39. package/dist/agent/observational-memory/plugin.d.ts.map +1 -0
  40. package/dist/agent/observational-memory/plugin.js +26 -0
  41. package/dist/agent/observational-memory/plugin.js.map +1 -0
  42. package/dist/agent/observational-memory/prompts.d.ts +27 -0
  43. package/dist/agent/observational-memory/prompts.d.ts.map +1 -0
  44. package/dist/agent/observational-memory/prompts.js +42 -0
  45. package/dist/agent/observational-memory/prompts.js.map +1 -0
  46. package/dist/agent/observational-memory/read.d.ts +47 -0
  47. package/dist/agent/observational-memory/read.d.ts.map +1 -0
  48. package/dist/agent/observational-memory/read.js +99 -0
  49. package/dist/agent/observational-memory/read.js.map +1 -0
  50. package/dist/agent/observational-memory/reflector.d.ts +31 -0
  51. package/dist/agent/observational-memory/reflector.d.ts.map +1 -0
  52. package/dist/agent/observational-memory/reflector.js +76 -0
  53. package/dist/agent/observational-memory/reflector.js.map +1 -0
  54. package/dist/agent/observational-memory/schema.d.ts +267 -0
  55. package/dist/agent/observational-memory/schema.d.ts.map +1 -0
  56. package/dist/agent/observational-memory/schema.js +48 -0
  57. package/dist/agent/observational-memory/schema.js.map +1 -0
  58. package/dist/agent/observational-memory/store.d.ts +52 -0
  59. package/dist/agent/observational-memory/store.d.ts.map +1 -0
  60. package/dist/agent/observational-memory/store.js +197 -0
  61. package/dist/agent/observational-memory/store.js.map +1 -0
  62. package/dist/agent/observational-memory/types.d.ts +61 -0
  63. package/dist/agent/observational-memory/types.d.ts.map +1 -0
  64. package/dist/agent/observational-memory/types.js +9 -0
  65. package/dist/agent/observational-memory/types.js.map +1 -0
  66. package/dist/agent/production-agent.d.ts +15 -0
  67. package/dist/agent/production-agent.d.ts.map +1 -1
  68. package/dist/agent/production-agent.js +240 -1
  69. package/dist/agent/production-agent.js.map +1 -1
  70. package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
  71. package/dist/agent/run-loop-with-resume.js +49 -0
  72. package/dist/agent/run-loop-with-resume.js.map +1 -1
  73. package/dist/agent/run-store.d.ts +17 -0
  74. package/dist/agent/run-store.d.ts.map +1 -1
  75. package/dist/agent/run-store.js +55 -0
  76. package/dist/agent/run-store.js.map +1 -1
  77. package/dist/agent/runtime-context.d.ts +30 -0
  78. package/dist/agent/runtime-context.d.ts.map +1 -1
  79. package/dist/agent/runtime-context.js +54 -1
  80. package/dist/agent/runtime-context.js.map +1 -1
  81. package/dist/agent/tool-call-journal.d.ts +101 -0
  82. package/dist/agent/tool-call-journal.d.ts.map +1 -0
  83. package/dist/agent/tool-call-journal.js +214 -0
  84. package/dist/agent/tool-call-journal.js.map +1 -0
  85. package/dist/agent/types.d.ts +24 -0
  86. package/dist/agent/types.d.ts.map +1 -1
  87. package/dist/agent/types.js.map +1 -1
  88. package/dist/cli/add.d.ts +109 -0
  89. package/dist/cli/add.d.ts.map +1 -0
  90. package/dist/cli/add.js +352 -0
  91. package/dist/cli/add.js.map +1 -0
  92. package/dist/cli/connect.d.ts +5 -4
  93. package/dist/cli/connect.d.ts.map +1 -1
  94. package/dist/cli/connect.js +157 -48
  95. package/dist/cli/connect.js.map +1 -1
  96. package/dist/cli/eval.d.ts +17 -0
  97. package/dist/cli/eval.d.ts.map +1 -0
  98. package/dist/cli/eval.js +121 -0
  99. package/dist/cli/eval.js.map +1 -0
  100. package/dist/cli/index.js +44 -3
  101. package/dist/cli/index.js.map +1 -1
  102. package/dist/cli/mcp-config-writers.d.ts +20 -13
  103. package/dist/cli/mcp-config-writers.d.ts.map +1 -1
  104. package/dist/cli/mcp-config-writers.js +152 -13
  105. package/dist/cli/mcp-config-writers.js.map +1 -1
  106. package/dist/cli/mcp.d.ts +2 -2
  107. package/dist/cli/mcp.d.ts.map +1 -1
  108. package/dist/cli/mcp.js +50 -196
  109. package/dist/cli/mcp.js.map +1 -1
  110. package/dist/cli/plan-local.d.ts +69 -6
  111. package/dist/cli/plan-local.d.ts.map +1 -1
  112. package/dist/cli/plan-local.js +517 -23
  113. package/dist/cli/plan-local.js.map +1 -1
  114. package/dist/cli/recap.d.ts.map +1 -1
  115. package/dist/cli/recap.js +1 -1
  116. package/dist/cli/recap.js.map +1 -1
  117. package/dist/cli/skills.d.ts +13 -6
  118. package/dist/cli/skills.d.ts.map +1 -1
  119. package/dist/cli/skills.js +287 -111
  120. package/dist/cli/skills.js.map +1 -1
  121. package/dist/client/AssistantChat.d.ts.map +1 -1
  122. package/dist/client/AssistantChat.js +118 -92
  123. package/dist/client/AssistantChat.js.map +1 -1
  124. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  125. package/dist/client/agent-chat-adapter.js +16 -0
  126. package/dist/client/agent-chat-adapter.js.map +1 -1
  127. package/dist/client/agent-engine-key.d.ts +6 -4
  128. package/dist/client/agent-engine-key.d.ts.map +1 -1
  129. package/dist/client/agent-engine-key.js +9 -6
  130. package/dist/client/agent-engine-key.js.map +1 -1
  131. package/dist/client/chat/run-recovery.js +1 -1
  132. package/dist/client/chat/run-recovery.js.map +1 -1
  133. package/dist/client/chat/tool-call-display.d.ts +20 -1
  134. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  135. package/dist/client/chat/tool-call-display.js +32 -7
  136. package/dist/client/chat/tool-call-display.js.map +1 -1
  137. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  138. package/dist/client/settings/SettingsPanel.js +7 -14
  139. package/dist/client/settings/SettingsPanel.js.map +1 -1
  140. package/dist/client/sse-event-processor.d.ts +13 -0
  141. package/dist/client/sse-event-processor.d.ts.map +1 -1
  142. package/dist/client/sse-event-processor.js +21 -0
  143. package/dist/client/sse-event-processor.js.map +1 -1
  144. package/dist/coding-tools/run-code.d.ts +7 -0
  145. package/dist/coding-tools/run-code.d.ts.map +1 -1
  146. package/dist/coding-tools/run-code.js +21 -106
  147. package/dist/coding-tools/run-code.js.map +1 -1
  148. package/dist/coding-tools/sandbox/adapter.d.ts +79 -0
  149. package/dist/coding-tools/sandbox/adapter.d.ts.map +1 -0
  150. package/dist/coding-tools/sandbox/adapter.js +24 -0
  151. package/dist/coding-tools/sandbox/adapter.js.map +1 -0
  152. package/dist/coding-tools/sandbox/index.d.ts +51 -0
  153. package/dist/coding-tools/sandbox/index.d.ts.map +1 -0
  154. package/dist/coding-tools/sandbox/index.js +79 -0
  155. package/dist/coding-tools/sandbox/index.js.map +1 -0
  156. package/dist/coding-tools/sandbox/local-child-process-adapter.d.ts +24 -0
  157. package/dist/coding-tools/sandbox/local-child-process-adapter.d.ts.map +1 -0
  158. package/dist/coding-tools/sandbox/local-child-process-adapter.js +141 -0
  159. package/dist/coding-tools/sandbox/local-child-process-adapter.js.map +1 -0
  160. package/dist/db/client.d.ts +4 -2
  161. package/dist/db/client.d.ts.map +1 -1
  162. package/dist/db/client.js +6 -4
  163. package/dist/db/client.js.map +1 -1
  164. package/dist/deploy/route-discovery.d.ts.map +1 -1
  165. package/dist/deploy/route-discovery.js +1 -0
  166. package/dist/deploy/route-discovery.js.map +1 -1
  167. package/dist/eval/agent-runner.d.ts +63 -0
  168. package/dist/eval/agent-runner.d.ts.map +1 -0
  169. package/dist/eval/agent-runner.js +142 -0
  170. package/dist/eval/agent-runner.js.map +1 -0
  171. package/dist/eval/define-eval.d.ts +29 -0
  172. package/dist/eval/define-eval.d.ts.map +1 -0
  173. package/dist/eval/define-eval.js +43 -0
  174. package/dist/eval/define-eval.js.map +1 -0
  175. package/dist/eval/index.d.ts +18 -0
  176. package/dist/eval/index.d.ts.map +1 -0
  177. package/dist/eval/index.js +17 -0
  178. package/dist/eval/index.js.map +1 -0
  179. package/dist/eval/report.d.ts +8 -0
  180. package/dist/eval/report.d.ts.map +1 -0
  181. package/dist/eval/report.js +44 -0
  182. package/dist/eval/report.js.map +1 -0
  183. package/dist/eval/runner.d.ts +67 -0
  184. package/dist/eval/runner.d.ts.map +1 -0
  185. package/dist/eval/runner.js +256 -0
  186. package/dist/eval/runner.js.map +1 -0
  187. package/dist/eval/scorer.d.ts +83 -0
  188. package/dist/eval/scorer.d.ts.map +1 -0
  189. package/dist/eval/scorer.js +195 -0
  190. package/dist/eval/scorer.js.map +1 -0
  191. package/dist/eval/types.d.ts +162 -0
  192. package/dist/eval/types.d.ts.map +1 -0
  193. package/dist/eval/types.js +20 -0
  194. package/dist/eval/types.js.map +1 -0
  195. package/dist/observability/traces.d.ts.map +1 -1
  196. package/dist/observability/traces.js +100 -1
  197. package/dist/observability/traces.js.map +1 -1
  198. package/dist/observability/tracing.d.ts +73 -0
  199. package/dist/observability/tracing.d.ts.map +1 -0
  200. package/dist/observability/tracing.js +126 -0
  201. package/dist/observability/tracing.js.map +1 -0
  202. package/dist/onboarding/default-steps.d.ts.map +1 -1
  203. package/dist/onboarding/default-steps.js +4 -1
  204. package/dist/onboarding/default-steps.js.map +1 -1
  205. package/dist/provider-api/actions/query-staged-dataset.d.ts +1 -1
  206. package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
  207. package/dist/scripts/agent-engines/list-agent-engines.js +10 -3
  208. package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
  209. package/dist/server/action-discovery.d.ts.map +1 -1
  210. package/dist/server/action-discovery.js +4 -0
  211. package/dist/server/action-discovery.js.map +1 -1
  212. package/dist/server/agent-chat-plugin.d.ts +9 -0
  213. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  214. package/dist/server/agent-chat-plugin.js +118 -110
  215. package/dist/server/agent-chat-plugin.js.map +1 -1
  216. package/dist/server/agent-engine-api-key-route.d.ts +37 -0
  217. package/dist/server/agent-engine-api-key-route.d.ts.map +1 -0
  218. package/dist/server/agent-engine-api-key-route.js +105 -0
  219. package/dist/server/agent-engine-api-key-route.js.map +1 -0
  220. package/dist/server/agent-teams.d.ts +62 -0
  221. package/dist/server/agent-teams.d.ts.map +1 -1
  222. package/dist/server/agent-teams.js +99 -2
  223. package/dist/server/agent-teams.js.map +1 -1
  224. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  225. package/dist/server/core-routes-plugin.js +17 -10
  226. package/dist/server/core-routes-plugin.js.map +1 -1
  227. package/dist/server/create-server.js +1 -1
  228. package/dist/server/create-server.js.map +1 -1
  229. package/dist/server/credential-provider.d.ts.map +1 -1
  230. package/dist/server/credential-provider.js +2 -0
  231. package/dist/server/credential-provider.js.map +1 -1
  232. package/dist/server/framework-request-handler.d.ts.map +1 -1
  233. package/dist/server/framework-request-handler.js +33 -1
  234. package/dist/server/framework-request-handler.js.map +1 -1
  235. package/dist/server/index.d.ts +1 -0
  236. package/dist/server/index.d.ts.map +1 -1
  237. package/dist/server/index.js +1 -0
  238. package/dist/server/index.js.map +1 -1
  239. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +17 -4
  240. package/dist/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +20 -0
  241. package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
  242. package/docs/content/agent-teams.md +32 -0
  243. package/docs/content/blueprint-installer.md +73 -0
  244. package/docs/content/evals.md +141 -0
  245. package/docs/content/pr-visual-recap.md +7 -4
  246. package/docs/content/sandbox-adapters.md +134 -0
  247. package/docs/content/template-plan.md +20 -8
  248. package/package.json +5 -1
  249. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +17 -4
  250. package/src/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +20 -0
  251. package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
@@ -1,12 +1,13 @@
1
1
  /**
2
- * `agent-native connect <url>` — wire your local Claude Code / Codex / Cowork
2
+ * `agent-native connect <url>` — wire your local MCP-capable coding agent
3
3
  * to a DEPLOYED agent-native app. OAuth-capable clients receive a standard
4
4
  * remote MCP URL entry and authenticate in the host. Fallback clients use the
5
5
  * browser device-code flow: open the verification URL, approve in the browser,
6
6
  * and the minted HTTP MCP server entry is written idempotently.
7
7
  *
8
- * agent-native connect <url> [--client all|claude-code|claude-code-cli|
9
- * codex|cowork] [--scope user|project]
8
+ * agent-native connect <url> [--client all|claude-code|
9
+ * codex|cowork|cursor|opencode|github-copilot]
10
+ * [--scope user|project]
10
11
  * [--name <serverName>]
11
12
  * agent-native reconnect [<url>] [--client ...] [--name <serverName>]
12
13
  * agent-native connect <url> --token <token> (no-browser fallback)
@@ -33,7 +34,7 @@ import os from "node:os";
33
34
  import { spawn } from "node:child_process";
34
35
  import path from "node:path";
35
36
  import { findWorkspaceRoot } from "../mcp/workspace-resolve.js";
36
- import { CLIENTS, configPathFor, removeSameUrlDuplicatesForClient, writeCodexBlock, writeHttpEntryForClient, writeJsonMcpEntry, } from "./mcp-config-writers.js";
37
+ import { CLIENTS, configPathFor, jsonMcpConfigKeyForClient, removeSameUrlDuplicatesForClient, writeCodexBlock, writeHttpEntryForClient, writeJsonMcpEntryForClient, } from "./mcp-config-writers.js";
37
38
  import { isFirstPartyPlanHost, writePlanPublishAuth, } from "./plan-publish-store.js";
38
39
  import { TEMPLATES, visibleTemplates } from "./templates-meta.js";
39
40
  const DEVICE_START_PATH = "/_agent-native/mcp/connect/device/start";
@@ -66,16 +67,25 @@ const CLIENT_LABELS = {
66
67
  "claude-code-cli": "Claude Code CLI",
67
68
  codex: "Codex",
68
69
  cowork: "Claude Cowork",
70
+ cursor: "Cursor",
71
+ opencode: "OpenCode",
72
+ "github-copilot": "GitHub Copilot / VS Code",
69
73
  };
70
74
  const CLIENT_HINTS = {
71
75
  "claude-code": ".mcp.json or ~/.claude.json",
72
76
  "claude-code-cli": ".mcp.json or ~/.claude.json",
73
77
  codex: "$CODEX_HOME/config.toml or ~/.codex/config.toml",
74
78
  cowork: "~/.cowork/mcp.json",
79
+ cursor: ".cursor/mcp.json or ~/.cursor/mcp.json",
80
+ opencode: "opencode.json or ~/.config/opencode/opencode.json",
81
+ "github-copilot": ".vscode/mcp.json or VS Code user mcp.json",
75
82
  };
76
83
  const REMOTE_MCP_OAUTH_CLIENTS = new Set([
77
84
  "claude-code",
78
85
  "claude-code-cli",
86
+ "cursor",
87
+ "opencode",
88
+ "github-copilot",
79
89
  ]);
80
90
  let logOutImpl = (msg) => process.stdout.write(`${msg}\n`);
81
91
  let logErrImpl = (msg) => process.stderr.write(`${msg}\n`);
@@ -175,11 +185,15 @@ export function normalizeUrl(raw) {
175
185
  const base = `${parsed.origin}${parsed.pathname}`.replace(/\/+$/, "");
176
186
  return base;
177
187
  }
188
+ // Clients offered in the interactive picker and expanded by "all". Excludes
189
+ // the `claude-code-cli` alias so users only ever see a single "Claude Code"
190
+ // option (it still works if passed explicitly via --client).
191
+ const SELECTABLE_CLIENTS = CLIENTS.filter((c) => c !== "claude-code-cli");
178
192
  /** Resolve the requested clients list. "all" → every supported client. */
179
193
  export function resolveClients(client) {
180
- const c = (client ?? "all").toLowerCase();
194
+ const c = normalizeClientAlias(client ?? "all");
181
195
  if (c === "all" || c === "")
182
- return [...CLIENTS];
196
+ return [...SELECTABLE_CLIENTS];
183
197
  if (c.includes(",")) {
184
198
  const clients = normalizeClientIds(c.split(",").map((part) => part.trim()));
185
199
  if (clients.length > 0)
@@ -189,6 +203,20 @@ export function resolveClients(client) {
189
203
  return [c];
190
204
  throw new Error(`Unknown --client "${client}". Use: all, ${CLIENTS.join(", ")}`);
191
205
  }
206
+ function normalizeClientAlias(value) {
207
+ const id = value.trim().toLowerCase();
208
+ // The Claude Code CLI and desktop share ~/.claude.json, so they are one
209
+ // client. `claude-code-cli` stays accepted for back-compat but collapses to
210
+ // the single "Claude Code" option everywhere it surfaces.
211
+ if (id === "claude" ||
212
+ id === "claude-code-desktop" ||
213
+ id === "claude-code-cli")
214
+ return "claude-code";
215
+ if (id === "copilot" || id === "vscode" || id === "vs-code") {
216
+ return "github-copilot";
217
+ }
218
+ return id;
219
+ }
192
220
  export function connectPreferencesPath() {
193
221
  return path.join(os.homedir(), ".agent-native", "connect.json");
194
222
  }
@@ -200,7 +228,7 @@ function normalizeClientIds(values) {
200
228
  for (const value of values) {
201
229
  if (typeof value !== "string")
202
230
  continue;
203
- const id = value.toLowerCase();
231
+ const id = normalizeClientAlias(value);
204
232
  if (!CLIENTS.includes(id))
205
233
  continue;
206
234
  const client = id;
@@ -233,7 +261,7 @@ export function writeConnectClientPreferences(clients, file = connectPreferences
233
261
  }, null, 2) + "\n", "utf-8");
234
262
  }
235
263
  function clientPromptOptions() {
236
- return CLIENTS.map((client) => ({
264
+ return SELECTABLE_CLIENTS.map((client) => ({
237
265
  value: client,
238
266
  label: CLIENT_LABELS[client],
239
267
  hint: CLIENT_HINTS[client],
@@ -361,6 +389,22 @@ function sentenceClientLabelList(clients) {
361
389
  return `${labels[0]} and ${labels[1]}`;
362
390
  return `${labels.slice(0, -1).join(", ")}, and ${labels[labels.length - 1]}`;
363
391
  }
392
+ function oauthNextStepsForClients(clients, serverName) {
393
+ const lines = [];
394
+ if (clients.includes("claude-code") || clients.includes("claude-code-cli")) {
395
+ lines.push("Claude Code: restart Claude Code, run /mcp, and choose Authenticate.");
396
+ }
397
+ if (clients.includes("cursor")) {
398
+ lines.push("Cursor: restart or reload Cursor, then authenticate the MCP server from Cursor MCP settings if prompted.");
399
+ }
400
+ if (clients.includes("opencode")) {
401
+ lines.push(`OpenCode: run opencode mcp auth ${serverName ?? "<server-name>"} or authenticate on first use.`);
402
+ }
403
+ if (clients.includes("github-copilot")) {
404
+ lines.push("GitHub Copilot / VS Code: reload VS Code, open the MCP config, and use the Auth action above the server if prompted.");
405
+ }
406
+ return lines;
407
+ }
364
408
  function clientsNotIn(requestedClients, effectiveClients) {
365
409
  const effective = new Set(effectiveClients);
366
410
  return requestedClients.filter((client) => !effective.has(client));
@@ -379,7 +423,7 @@ async function showReconnectSuccessOutro({ serverName, clients, }) {
379
423
  }
380
424
  const oauthClients = clients.filter((client) => supportsRemoteMcpOAuth(client));
381
425
  if (oauthClients.length > 0) {
382
- lines.push(`${sentenceClientLabelList(oauthClients)}: restart, run /mcp, then choose Authenticate/Reconnect.`);
426
+ lines.push(...oauthNextStepsForClients(oauthClients, serverName));
383
427
  }
384
428
  if (!clients.includes("codex") && oauthClients.length === 0) {
385
429
  lines.push(`Restart or reload ${sentenceClientLabelList(clients)} before using the MCP tools.`);
@@ -556,26 +600,43 @@ export async function runDeviceFlow(baseUrl, appSlug, clientArg, deps = {}, opti
556
600
  const sleep = deps.sleep ?? realSleep;
557
601
  const open = deps.openBrowser ?? openInBrowser;
558
602
  const now = deps.now ?? (() => Date.now());
559
- let start;
560
- try {
561
- const { status, json } = await postJson(fetchImpl, `${baseUrl}${DEVICE_START_PATH}`, {
562
- client: clientArg,
563
- app: appSlug,
564
- ...(options.fullCatalog ? { fullCatalog: true } : {}),
565
- });
566
- if (status < 200 || status >= 300 || !json?.device_code) {
603
+ let start = null;
604
+ // A cold/propagating Plan instance can briefly 404/5xx before its connect
605
+ // route is registered (async plugin init). Retry a few times so a recoverable
606
+ // blip doesn't kill the connect before polling even begins.
607
+ const START_ATTEMPTS = 4;
608
+ for (let attempt = 0; attempt < START_ATTEMPTS; attempt++) {
609
+ try {
610
+ const { status, json } = await postJson(fetchImpl, `${baseUrl}${DEVICE_START_PATH}`, {
611
+ client: clientArg,
612
+ app: appSlug,
613
+ ...(options.fullCatalog ? { fullCatalog: true } : {}),
614
+ });
615
+ if (status >= 200 && status < 300 && json?.device_code) {
616
+ start = json;
617
+ break;
618
+ }
619
+ if ((status === 404 || status >= 500) && attempt < START_ATTEMPTS - 1) {
620
+ await sleep(1000 * (attempt + 1));
621
+ continue;
622
+ }
567
623
  logErr(` Could not start the connect flow on ${baseUrl} ` +
568
624
  `(HTTP ${status}). Is this an agent-native app, and is it ` +
569
625
  `deployed with the connect endpoint enabled?`);
570
626
  return null;
571
627
  }
572
- start = json;
628
+ catch (err) {
629
+ if (attempt < START_ATTEMPTS - 1) {
630
+ await sleep(1000 * (attempt + 1));
631
+ continue;
632
+ }
633
+ logErr(` Could not reach ${baseUrl} (${err?.message ?? err}). ` +
634
+ `Check the URL and your network.`);
635
+ return null;
636
+ }
573
637
  }
574
- catch (err) {
575
- logErr(` Could not reach ${baseUrl} (${err?.message ?? err}). ` +
576
- `Check the URL and your network.`);
638
+ if (!start)
577
639
  return null;
578
- }
579
640
  const interval = Math.max(1, Number(start.interval) || 5);
580
641
  const expiresIn = Math.max(interval, Number(start.expires_in) || 600);
581
642
  const deadline = now() + expiresIn * 1000;
@@ -588,23 +649,59 @@ export async function runDeviceFlow(baseUrl, appSlug, clientArg, deps = {}, opti
588
649
  logOut(" Approve in the browser to finish. Opening it now…");
589
650
  open(start.verification_uri_complete);
590
651
  let spin = 0;
652
+ let transientStreak = 0;
653
+ // Ride out brief cold-instance blips, but don't poll a persistently-dead
654
+ // endpoint forever: give up after this many consecutive transient (404/5xx
655
+ // or network-error) polls. Reset as soon as one poll responds normally.
656
+ const MAX_TRANSIENT_POLLS = 20;
591
657
  const isTTY = !!process.stdout.isTTY;
592
658
  while (now() < deadline) {
593
659
  let poll;
660
+ let transient = false;
594
661
  try {
595
662
  const { status, json } = await postJson(fetchImpl, `${baseUrl}${DEVICE_POLL_PATH}`, { device_code: start.device_code });
596
663
  if (status < 200 || status >= 300) {
664
+ if (isTerminalPollBody(json)) {
665
+ poll = json;
666
+ }
667
+ else if (status === 404 || status >= 500) {
668
+ // Transient: a cold/propagating Plan instance can briefly serve a
669
+ // bare 404 (the MCP route isn't registered until async plugin init
670
+ // settles) or a 5xx before it's healthy. The next poll usually lands
671
+ // on a warm instance, so keep polling until the deadline instead of
672
+ // hard-failing the whole connect on a recoverable blip. (This is the
673
+ // recurring "Cannot find any route matching [POST] .../mcp" case.)
674
+ poll = { status: "pending" };
675
+ transient = true;
676
+ }
677
+ else {
678
+ if (isTTY)
679
+ process.stdout.write("\r\x1b[K");
680
+ logErr(` Connect polling failed (HTTP ${status}): ` +
681
+ responseMessage(json, "server returned an error."));
682
+ return null;
683
+ }
684
+ }
685
+ else {
686
+ poll = (json ?? { status: "pending" });
687
+ }
688
+ }
689
+ catch {
690
+ // Transient network error — keep polling.
691
+ poll = { status: "pending" };
692
+ transient = true;
693
+ }
694
+ if (transient) {
695
+ if (++transientStreak > MAX_TRANSIENT_POLLS) {
597
696
  if (isTTY)
598
697
  process.stdout.write("\r\x1b[K");
599
- logErr(` Connect polling failed (HTTP ${status}): ` +
600
- responseMessage(json, "server returned an error."));
698
+ logErr(" Connect endpoint is not responding (repeated 404/5xx). It may be " +
699
+ "mid-deploy wait a minute and run the command again.");
601
700
  return null;
602
701
  }
603
- poll = (json ?? { status: "pending" });
604
702
  }
605
- catch {
606
- // Transient network error — keep polling until the deadline.
607
- poll = { status: "pending" };
703
+ else {
704
+ transientStreak = 0;
608
705
  }
609
706
  if (poll.status === "approved") {
610
707
  if (isTTY)
@@ -652,6 +749,12 @@ export async function runDeviceFlow(baseUrl, appSlug, clientArg, deps = {}, opti
652
749
  logErr(" Timed out waiting for approval. Run the command again to retry.");
653
750
  return null;
654
751
  }
752
+ function isTerminalPollBody(json) {
753
+ return (json?.status === "not_found" ||
754
+ json?.status === "error" ||
755
+ json?.status === "expired" ||
756
+ json?.status === "consumed");
757
+ }
655
758
  // ---------------------------------------------------------------------------
656
759
  // Writing config(s)
657
760
  // ---------------------------------------------------------------------------
@@ -707,10 +810,10 @@ function setSavedProfileEntry(profiles, serverName, client, file, entry) {
707
810
  profiles.prodEntries[serverName][client] ??= {};
708
811
  profiles.prodEntries[serverName][client][file] = entry;
709
812
  }
710
- function readJsonMcpServerEntry(file, serverName) {
813
+ function readJsonMcpServerEntry(client, file, serverName) {
711
814
  try {
712
815
  const parsed = JSON.parse(fs.readFileSync(file, "utf-8"));
713
- const entry = parsed?.mcpServers?.[serverName];
816
+ const entry = parsed?.[jsonMcpConfigKeyForClient(client)]?.[serverName];
714
817
  return entry && typeof entry === "object" ? entry : undefined;
715
818
  }
716
819
  catch {
@@ -760,7 +863,7 @@ function readCurrentMcpEntry(client, serverName, baseDir, scope) {
760
863
  : undefined,
761
864
  };
762
865
  }
763
- const entry = readJsonMcpServerEntry(file, serverName);
866
+ const entry = readJsonMcpServerEntry(client, file, serverName);
764
867
  return {
765
868
  file,
766
869
  saved: entry
@@ -777,7 +880,7 @@ function writeSavedMcpEntry(client, file, serverName, saved) {
777
880
  }
778
881
  if (saved.kind !== "json")
779
882
  return;
780
- writeJsonMcpEntry(file, serverName, saved.entry);
883
+ writeJsonMcpEntryForClient(client, file, serverName, saved.entry);
781
884
  }
782
885
  function unescapeTomlString(value) {
783
886
  return value.replace(/\\"/g, '"').replace(/\\\\/g, "\\");
@@ -808,10 +911,10 @@ function savedEntryUrl(saved) {
808
911
  const match = saved.block.match(/^\s*url\s*=\s*"((?:\\.|[^"])*)"/m);
809
912
  return match ? unescapeTomlString(match[1]) : undefined;
810
913
  }
811
- function readJsonMcpServerEntries(file) {
914
+ function readJsonMcpServerEntries(client, file) {
812
915
  try {
813
916
  const parsed = JSON.parse(fs.readFileSync(file, "utf-8"));
814
- const servers = parsed?.mcpServers;
917
+ const servers = parsed?.[jsonMcpConfigKeyForClient(client)];
815
918
  if (!servers || typeof servers !== "object" || Array.isArray(servers)) {
816
919
  return [];
817
920
  }
@@ -881,7 +984,7 @@ function readExistingMcpEntries(clients, baseDir, scope) {
881
984
  const file = configPathFor(client, baseDir, scope);
882
985
  const rawEntries = client === "codex"
883
986
  ? readCodexMcpServerEntries(file)
884
- : readJsonMcpServerEntries(file);
987
+ : readJsonMcpServerEntries(client, file);
885
988
  for (const { serverName, saved } of rawEntries) {
886
989
  const url = savedEntryUrl(saved);
887
990
  if (!url)
@@ -912,7 +1015,12 @@ function savedEntryHeaders(saved) {
912
1015
  if (!saved)
913
1016
  return {};
914
1017
  if (saved.kind === "json") {
915
- const headers = saved.entry.headers;
1018
+ const headers = saved.entry.headers ??
1019
+ (saved.entry.requestInit &&
1020
+ typeof saved.entry.requestInit === "object" &&
1021
+ !Array.isArray(saved.entry.requestInit)
1022
+ ? saved.entry.requestInit.headers
1023
+ : undefined);
916
1024
  return headers && typeof headers === "object"
917
1025
  ? Object.fromEntries(Object.entries(headers)
918
1026
  .filter((entry) => {
@@ -1526,12 +1634,11 @@ async function connectOne(rawUrl, parsed, clients, deps) {
1526
1634
  // ADDITIONAL write alongside the per-client MCP config; Best-effort and
1527
1635
  // merge-not-clobber — never fails the connect.
1528
1636
  //
1529
- // OAuth clients (claude-code, claude-code-cli) authenticate in-host via
1530
- // standard MCP OAuth, so they never mint a local bearer token. To still
1531
- // populate the publish store for them, we run a supplemental device-flow
1532
- // mint using a non-OAuth client arg so the Plans server gets a usable token
1533
- // and `publish-visual-plan` doesn't send the user back to `agent-native
1534
- // connect` right after they just ran it.
1637
+ // OAuth clients authenticate in-host via standard MCP OAuth, so they never
1638
+ // mint a local bearer token. To still populate the publish store for them, we
1639
+ // run a supplemental device-flow mint using a non-OAuth client arg so the
1640
+ // Plans server gets a usable token and `publish-visual-plan` doesn't send the
1641
+ // user back to `agent-native connect` right after they just ran it.
1535
1642
  let publishToken = token;
1536
1643
  if (!publishToken &&
1537
1644
  oauthClients.length > 0 &&
@@ -1579,7 +1686,9 @@ async function connectOne(rawUrl, parsed, clients, deps) {
1579
1686
  logOut(` Replaced legacy bearer headers for ${clientLabelList(oauthMigrations)}; it will reconnect with standard MCP OAuth.`);
1580
1687
  }
1581
1688
  logOut(` ${clientLabelList(oauthClients)}: wrote URL-only MCP config (no bearer headers).`);
1582
- logOut(" Next: restart Claude Code, run /mcp, and choose Authenticate.");
1689
+ for (const line of oauthNextStepsForClients(oauthClients, serverName)) {
1690
+ logOut(` Next: ${line}`);
1691
+ }
1583
1692
  }
1584
1693
  logOut("");
1585
1694
  logOut(` Restart or reload ${sentenceClientLabelList(clients)} to pick up the new MCP server.`);
@@ -1762,16 +1871,16 @@ Usage:
1762
1871
 
1763
1872
  npx @agent-native/core@latest connect <url> [--client <c>] [--scope user|project] [--name <n>]
1764
1873
  Writes the HTTP MCP entry into your selected client config(s). Claude
1765
- Code / Claude Code CLI use standard remote MCP OAuth: restart Claude,
1766
- run /mcp, and choose Authenticate. Codex / Cowork use the browser
1874
+ Code, Cursor, OpenCode, and GitHub Copilot / VS Code use standard remote
1875
+ MCP OAuth and get URL-only config. Codex / Cowork use the browser
1767
1876
  device-code fallback: the command prints a code, opens the verification
1768
1877
  URL, polls until approved, then writes bearer headers. With no --client,
1769
1878
  opens a brief picker preselected from ~/.agent-native/connect.json, or
1770
1879
  all clients on first run. Idempotent — re-running replaces the same entry.
1771
1880
  Auth is stored per client config/session; restart or reload each selected
1772
1881
  client before expecting new tools to appear.
1773
- Re-running over an older Claude bearer entry upgrades it to URL-only
1774
- OAuth config and prompts you to authenticate with /mcp.
1882
+ Re-running over an older OAuth-capable bearer entry upgrades it to
1883
+ URL-only OAuth config and prompts you to authenticate in that host.
1775
1884
 
1776
1885
  For cross-app access, prefer the unified Dispatch gateway:
1777
1886
  npx @agent-native/core@latest connect https://dispatch.agent-native.com
@@ -1808,7 +1917,7 @@ Developer:
1808
1917
  npx @agent-native/core@latest connect prod [--apps mail,calendar] [--client <c>]
1809
1918
  Restore production MCP entries saved before the dev switch.
1810
1919
 
1811
- Clients: all (default), claude-code, claude-code-cli, codex, cowork
1920
+ Clients: all (default), claude-code, codex, cowork, cursor, opencode, github-copilot
1812
1921
  Scope: user (default, ~/.claude.json) or project (.mcp.json)`;
1813
1922
  /**
1814
1923
  * `agent-native connect` entry point. `deps` is injectable for tests; the