@agent-native/core 0.17.1 → 0.18.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 (78) hide show
  1. package/dist/action.d.ts +27 -0
  2. package/dist/action.d.ts.map +1 -1
  3. package/dist/action.js +2 -0
  4. package/dist/action.js.map +1 -1
  5. package/dist/agent/production-agent.d.ts +4 -0
  6. package/dist/agent/production-agent.d.ts.map +1 -1
  7. package/dist/agent/production-agent.js.map +1 -1
  8. package/dist/cli/index.js +16 -0
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/cli/mcp.d.ts +16 -0
  11. package/dist/cli/mcp.d.ts.map +1 -0
  12. package/dist/cli/mcp.js +583 -0
  13. package/dist/cli/mcp.js.map +1 -0
  14. package/dist/db/client.d.ts +27 -0
  15. package/dist/db/client.d.ts.map +1 -1
  16. package/dist/db/client.js +62 -19
  17. package/dist/db/client.js.map +1 -1
  18. package/dist/db/create-get-db.d.ts.map +1 -1
  19. package/dist/db/create-get-db.js +6 -9
  20. package/dist/db/create-get-db.js.map +1 -1
  21. package/dist/db/index.d.ts.map +1 -1
  22. package/dist/db/index.js +2 -1
  23. package/dist/db/index.js.map +1 -1
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/mcp/build-server.d.ts +135 -0
  28. package/dist/mcp/build-server.d.ts.map +1 -0
  29. package/dist/mcp/build-server.js +274 -0
  30. package/dist/mcp/build-server.js.map +1 -0
  31. package/dist/mcp/builtin-tools.d.ts +32 -0
  32. package/dist/mcp/builtin-tools.d.ts.map +1 -0
  33. package/dist/mcp/builtin-tools.js +299 -0
  34. package/dist/mcp/builtin-tools.js.map +1 -0
  35. package/dist/mcp/index.d.ts +7 -0
  36. package/dist/mcp/index.d.ts.map +1 -1
  37. package/dist/mcp/index.js +8 -0
  38. package/dist/mcp/index.js.map +1 -1
  39. package/dist/mcp/server.d.ts +3 -13
  40. package/dist/mcp/server.d.ts.map +1 -1
  41. package/dist/mcp/server.js +21 -175
  42. package/dist/mcp/server.js.map +1 -1
  43. package/dist/mcp/stdio.d.ts +44 -0
  44. package/dist/mcp/stdio.d.ts.map +1 -0
  45. package/dist/mcp/stdio.js +208 -0
  46. package/dist/mcp/stdio.js.map +1 -0
  47. package/dist/mcp/workspace-resolve.d.ts +68 -0
  48. package/dist/mcp/workspace-resolve.d.ts.map +1 -0
  49. package/dist/mcp/workspace-resolve.js +205 -0
  50. package/dist/mcp/workspace-resolve.js.map +1 -0
  51. package/dist/server/action-discovery.d.ts.map +1 -1
  52. package/dist/server/action-discovery.js +3 -0
  53. package/dist/server/action-discovery.js.map +1 -1
  54. package/dist/server/auth.d.ts +9 -0
  55. package/dist/server/auth.d.ts.map +1 -1
  56. package/dist/server/auth.js +25 -0
  57. package/dist/server/auth.js.map +1 -1
  58. package/dist/server/better-auth-instance.d.ts.map +1 -1
  59. package/dist/server/better-auth-instance.js +15 -10
  60. package/dist/server/better-auth-instance.js.map +1 -1
  61. package/dist/server/core-routes-plugin.d.ts +5 -0
  62. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  63. package/dist/server/core-routes-plugin.js +9 -0
  64. package/dist/server/core-routes-plugin.js.map +1 -1
  65. package/dist/server/deep-link.d.ts +55 -0
  66. package/dist/server/deep-link.d.ts.map +1 -0
  67. package/dist/server/deep-link.js +69 -0
  68. package/dist/server/deep-link.js.map +1 -0
  69. package/dist/server/index.d.ts +2 -0
  70. package/dist/server/index.d.ts.map +1 -1
  71. package/dist/server/index.js +2 -0
  72. package/dist/server/index.js.map +1 -1
  73. package/dist/server/open-route.d.ts +12 -0
  74. package/dist/server/open-route.d.ts.map +1 -0
  75. package/dist/server/open-route.js +128 -0
  76. package/dist/server/open-route.js.map +1 -0
  77. package/docs/content/external-agents.md +177 -0
  78. package/package.json +1 -1
@@ -0,0 +1,583 @@
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
+ import crypto from "node:crypto";
16
+ import fs from "node:fs";
17
+ import os from "node:os";
18
+ import path from "node:path";
19
+ import { runMCPStdio } from "../mcp/stdio.js";
20
+ import { findWorkspaceRoot, resolveLocalAppOrigin, resolveWorkspace, } from "../mcp/workspace-resolve.js";
21
+ const SERVER_NAME_PREFIX = "agent-native";
22
+ const CLIENTS = [
23
+ "claude-code",
24
+ "claude-code-cli",
25
+ "codex",
26
+ "cowork",
27
+ ];
28
+ function parseArgs(argv) {
29
+ const out = { _: [], standalone: false, rotate: false };
30
+ for (let i = 0; i < argv.length; i++) {
31
+ const a = argv[i];
32
+ const eat = (flag) => {
33
+ if (a === flag)
34
+ return argv[++i];
35
+ if (a.startsWith(`${flag}=`))
36
+ return a.slice(flag.length + 1);
37
+ return undefined;
38
+ };
39
+ let v;
40
+ if ((v = eat("--client")) !== undefined)
41
+ out.client = v;
42
+ else if ((v = eat("--app")) !== undefined)
43
+ out.app = v;
44
+ else if ((v = eat("--port")) !== undefined)
45
+ out.port = Number(v);
46
+ else if ((v = eat("--scope")) !== undefined)
47
+ out.scope = v;
48
+ else if (a === "--standalone")
49
+ out.standalone = true;
50
+ else if (a === "--rotate")
51
+ out.rotate = true;
52
+ else if (!a.startsWith("-"))
53
+ out._.push(a);
54
+ }
55
+ return out;
56
+ }
57
+ function logErr(msg) {
58
+ process.stderr.write(`${msg}\n`);
59
+ }
60
+ function logOut(msg) {
61
+ process.stdout.write(`${msg}\n`);
62
+ }
63
+ // ---------------------------------------------------------------------------
64
+ // .env token provisioning (local dev) — hand-rolled idempotent upsert
65
+ // ---------------------------------------------------------------------------
66
+ /** Workspace root (or cwd for a standalone app) — where .env lives. */
67
+ function envBaseDir(cwd = process.cwd()) {
68
+ return findWorkspaceRoot(cwd) ?? path.resolve(cwd);
69
+ }
70
+ /** Prefer .env.local, else .env. Returns the path we should write to. */
71
+ function envFilePath(baseDir) {
72
+ const local = path.join(baseDir, ".env.local");
73
+ if (fs.existsSync(local))
74
+ return local;
75
+ return path.join(baseDir, ".env");
76
+ }
77
+ function readEnvFile(file) {
78
+ try {
79
+ return fs.readFileSync(file, "utf-8");
80
+ }
81
+ catch {
82
+ return "";
83
+ }
84
+ }
85
+ /** Read a single key from a dotenv-format string (last assignment wins). */
86
+ function getEnvValue(content, key) {
87
+ let found;
88
+ for (const line of content.split(/\r?\n/)) {
89
+ const m = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)\s*$/i);
90
+ if (m && m[1] === key) {
91
+ found = m[2].replace(/^["']|["']$/g, "");
92
+ }
93
+ }
94
+ return found;
95
+ }
96
+ /**
97
+ * Idempotently set `key=value` in the dotenv file. If the key already exists
98
+ * we leave it untouched unless `force` is set (used by `token --rotate`).
99
+ * Never clobbers an existing token implicitly.
100
+ */
101
+ function upsertEnv(file, key, value, force = false) {
102
+ const content = readEnvFile(file);
103
+ const existing = getEnvValue(content, key);
104
+ if (existing && !force)
105
+ return { changed: false, value: existing };
106
+ const line = `${key}=${value}`;
107
+ let next;
108
+ if (new RegExp(`^\\s*${key}\\s*=`, "m").test(content)) {
109
+ next = content.replace(new RegExp(`^\\s*${key}\\s*=.*$`, "m"), line);
110
+ }
111
+ else {
112
+ next =
113
+ content.length === 0
114
+ ? `${line}\n`
115
+ : `${content.replace(/\n*$/, "")}\n${line}\n`;
116
+ }
117
+ fs.mkdirSync(path.dirname(file), { recursive: true });
118
+ fs.writeFileSync(file, next, "utf-8");
119
+ return { changed: true, value };
120
+ }
121
+ function generateToken() {
122
+ return crypto.randomBytes(24).toString("base64url");
123
+ }
124
+ /**
125
+ * Ensure a local ACCESS_TOKEN exists in the workspace .env and return it.
126
+ * Existing tokens are reused (never clobbered). Set `rotate` to replace it.
127
+ */
128
+ function ensureLocalToken(cwd, rotate = false) {
129
+ const baseDir = envBaseDir(cwd);
130
+ const file = envFilePath(baseDir);
131
+ const content = readEnvFile(file);
132
+ const existing = getEnvValue(content, "ACCESS_TOKEN");
133
+ if (existing && !rotate) {
134
+ return { token: existing, file, created: false };
135
+ }
136
+ const token = generateToken();
137
+ upsertEnv(file, "ACCESS_TOKEN", token, true);
138
+ return { token, file, created: true };
139
+ }
140
+ // ---------------------------------------------------------------------------
141
+ // Hosted vs local detection
142
+ // ---------------------------------------------------------------------------
143
+ /**
144
+ * Detect a hosted deployment URL. When the workspace .env points at a hosted
145
+ * origin (APP_URL / BETTER_AUTH_URL with a non-localhost host) we write an
146
+ * `http` client entry pointing at `<origin>/_agent-native/mcp` with a JWT
147
+ * bearer instead of a stdio entry.
148
+ */
149
+ function detectHostedUrl(cwd) {
150
+ const baseDir = envBaseDir(cwd);
151
+ const content = readEnvFile(path.join(baseDir, ".env.local")) +
152
+ "\n" +
153
+ readEnvFile(path.join(baseDir, ".env"));
154
+ for (const key of ["AGENT_NATIVE_MCP_URL", "APP_URL", "BETTER_AUTH_URL"]) {
155
+ const v = getEnvValue(content, key);
156
+ if (!v)
157
+ continue;
158
+ try {
159
+ const u = new URL(v);
160
+ if (!/^(localhost|127\.0\.0\.1|\[::1\])$/.test(u.hostname)) {
161
+ return `${u.origin}/_agent-native/mcp`;
162
+ }
163
+ }
164
+ catch {
165
+ // not a URL — skip
166
+ }
167
+ }
168
+ return undefined;
169
+ }
170
+ async function mintHostedJwt(cwd) {
171
+ // Reuse the existing A2A signer — do not reinvent JWT minting.
172
+ const owner = process.env.AGENT_NATIVE_OWNER_EMAIL ||
173
+ process.env.OWNER_EMAIL ||
174
+ "owner@localhost";
175
+ if (!process.env.A2A_SECRET) {
176
+ const baseDir = envBaseDir(cwd);
177
+ const content = readEnvFile(path.join(baseDir, ".env.local")) +
178
+ "\n" +
179
+ readEnvFile(path.join(baseDir, ".env"));
180
+ const secret = getEnvValue(content, "A2A_SECRET");
181
+ if (secret)
182
+ process.env.A2A_SECRET = secret;
183
+ }
184
+ try {
185
+ const { signA2AToken } = await import("../a2a/client.js");
186
+ return await signA2AToken(owner, undefined, undefined, {
187
+ preferGlobalSecret: true,
188
+ expiresIn: "30d",
189
+ });
190
+ }
191
+ catch (err) {
192
+ logErr(` Could not mint a hosted JWT (${err?.message ?? err}). ` +
193
+ `Set A2A_SECRET in your workspace .env, or use the local stdio entry.`);
194
+ return undefined;
195
+ }
196
+ }
197
+ // ---------------------------------------------------------------------------
198
+ // Client config file locations + writers
199
+ // ---------------------------------------------------------------------------
200
+ /**
201
+ * Cowork consumes MCP exactly like Claude Code (same JSON server-entry
202
+ * shape). The exact on-disk config path for Cowork may differ across builds —
203
+ * this is the best-known location. **Confirm before relying on it in
204
+ * production.** It is validated against the Claude Code JSON format below.
205
+ *
206
+ * Resolved lazily (not as a module-level constant) so `os.homedir()` reflects
207
+ * the current `$HOME` rather than the value at module-load time.
208
+ */
209
+ function coworkConfigPath() {
210
+ return path.join(os.homedir(), ".cowork", "mcp.json");
211
+ }
212
+ function claudeCodeProjectConfig(cwd) {
213
+ return path.join(envBaseDir(cwd), ".mcp.json");
214
+ }
215
+ function claudeCodeUserConfig() {
216
+ return path.join(os.homedir(), ".claude.json");
217
+ }
218
+ function codexConfigPath() {
219
+ return path.join(os.homedir(), ".codex", "config.toml");
220
+ }
221
+ /** The stdio (or http) server entry — shared by Claude Code & Cowork JSON. */
222
+ function buildJsonServerEntry(i) {
223
+ if (i.hostedUrl) {
224
+ return {
225
+ type: "http",
226
+ url: i.hostedUrl,
227
+ ...(i.token ? { headers: { Authorization: `Bearer ${i.token}` } } : {}),
228
+ };
229
+ }
230
+ const args = ["mcp", "serve"];
231
+ if (i.appId)
232
+ args.push("--app", i.appId);
233
+ if (i.standalone)
234
+ args.push("--standalone");
235
+ const env = {};
236
+ if (i.token)
237
+ env.ACCESS_TOKEN = i.token;
238
+ if (i.ownerEmail)
239
+ env.AGENT_NATIVE_OWNER_EMAIL = i.ownerEmail;
240
+ return {
241
+ command: "agent-native",
242
+ args,
243
+ ...(Object.keys(env).length ? { env } : {}),
244
+ };
245
+ }
246
+ function readJsonFile(file) {
247
+ try {
248
+ const raw = fs.readFileSync(file, "utf-8");
249
+ const parsed = JSON.parse(raw);
250
+ return parsed && typeof parsed === "object" ? parsed : {};
251
+ }
252
+ catch {
253
+ return {};
254
+ }
255
+ }
256
+ /** Idempotently write `mcpServers[name] = entry` into a JSON config file. */
257
+ function writeJsonMcpEntry(file, name, entry) {
258
+ const config = readJsonFile(file);
259
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
260
+ config.mcpServers = {};
261
+ }
262
+ if (entry === null) {
263
+ delete config.mcpServers[name];
264
+ }
265
+ else {
266
+ config.mcpServers[name] = entry;
267
+ }
268
+ fs.mkdirSync(path.dirname(file), { recursive: true });
269
+ fs.writeFileSync(file, JSON.stringify(config, null, 2) + "\n", "utf-8");
270
+ }
271
+ function hasJsonMcpEntry(file, name) {
272
+ const config = readJsonFile(file);
273
+ return !!config?.mcpServers && name in config.mcpServers;
274
+ }
275
+ // --- Codex TOML (hand-rolled minimal block merge, no new dep) -------------
276
+ function tomlQuote(s) {
277
+ return `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
278
+ }
279
+ function buildCodexBlock(name, i) {
280
+ const lines = [`[mcp_servers.${name}]`];
281
+ const args = ["mcp", "serve"];
282
+ if (i.appId)
283
+ args.push("--app", i.appId);
284
+ if (i.standalone)
285
+ args.push("--standalone");
286
+ lines.push(`command = "agent-native"`);
287
+ lines.push(`args = [${args.map(tomlQuote).join(", ")}]`);
288
+ const env = {};
289
+ if (i.token)
290
+ env.ACCESS_TOKEN = i.token;
291
+ if (i.ownerEmail)
292
+ env.AGENT_NATIVE_OWNER_EMAIL = i.ownerEmail;
293
+ if (Object.keys(env).length) {
294
+ const inline = Object.entries(env)
295
+ .map(([k, v]) => `${k} = ${tomlQuote(v)}`)
296
+ .join(", ");
297
+ lines.push(`env = { ${inline} }`);
298
+ }
299
+ return lines.join("\n") + "\n";
300
+ }
301
+ /**
302
+ * Replace (or append) the `[mcp_servers.<name>]` block in a TOML file
303
+ * without disturbing other content. We treat a block as the header line plus
304
+ * every following line until the next top-level `[` table header or EOF.
305
+ */
306
+ function writeCodexBlock(file, name, block) {
307
+ let content = "";
308
+ try {
309
+ content = fs.readFileSync(file, "utf-8");
310
+ }
311
+ catch {
312
+ content = "";
313
+ }
314
+ const header = `[mcp_servers.${name}]`;
315
+ const lines = content.split(/\r?\n/);
316
+ const out = [];
317
+ let i = 0;
318
+ let removed = false;
319
+ while (i < lines.length) {
320
+ const line = lines[i];
321
+ if (line.trim() === header) {
322
+ // Skip this block entirely (header + body until next table header).
323
+ removed = true;
324
+ i++;
325
+ while (i < lines.length && !/^\s*\[/.test(lines[i]))
326
+ i++;
327
+ continue;
328
+ }
329
+ out.push(line);
330
+ i++;
331
+ }
332
+ let next = out
333
+ .join("\n")
334
+ .replace(/\n{3,}/g, "\n\n")
335
+ .replace(/\n*$/, "\n");
336
+ if (block !== null) {
337
+ next = next.replace(/\n*$/, "\n");
338
+ if (next.trim().length)
339
+ next += "\n";
340
+ next += block;
341
+ }
342
+ if (block === null && !removed)
343
+ return; // nothing to do
344
+ fs.mkdirSync(path.dirname(file), { recursive: true });
345
+ fs.writeFileSync(file, next, "utf-8");
346
+ }
347
+ function codexHasBlock(file, name) {
348
+ try {
349
+ const content = fs.readFileSync(file, "utf-8");
350
+ return new RegExp(`^\\s*\\[mcp_servers\\.${name}\\]\\s*$`, "m").test(content);
351
+ }
352
+ catch {
353
+ return false;
354
+ }
355
+ }
356
+ // ---------------------------------------------------------------------------
357
+ // Per-client install/uninstall/status
358
+ // ---------------------------------------------------------------------------
359
+ function configPathFor(client, cwd, scope) {
360
+ switch (client) {
361
+ case "claude-code":
362
+ case "claude-code-cli":
363
+ return scope === "user"
364
+ ? claudeCodeUserConfig()
365
+ : claudeCodeProjectConfig(cwd);
366
+ case "cowork":
367
+ return coworkConfigPath();
368
+ case "codex":
369
+ return codexConfigPath();
370
+ }
371
+ }
372
+ function serverNameFor(appId) {
373
+ return `${SERVER_NAME_PREFIX}-${appId}`;
374
+ }
375
+ function installForClient(client, inputs, cwd, scope) {
376
+ const name = inputs.serverName;
377
+ const file = configPathFor(client, cwd, scope);
378
+ if (client === "codex") {
379
+ writeCodexBlock(file, name, buildCodexBlock(name, inputs));
380
+ }
381
+ else {
382
+ writeJsonMcpEntry(file, name, buildJsonServerEntry(inputs));
383
+ }
384
+ return file;
385
+ }
386
+ function uninstallForClient(client, appId, cwd, scope) {
387
+ const name = serverNameFor(appId);
388
+ const file = configPathFor(client, cwd, scope);
389
+ if (client === "codex") {
390
+ const had = codexHasBlock(file, name);
391
+ if (had)
392
+ writeCodexBlock(file, name, null);
393
+ return { file, removed: had };
394
+ }
395
+ const had = hasJsonMcpEntry(file, name);
396
+ if (had)
397
+ writeJsonMcpEntry(file, name, null);
398
+ return { file, removed: had };
399
+ }
400
+ function clientHasEntry(client, appId, cwd) {
401
+ const name = serverNameFor(appId);
402
+ // Check both scopes for Claude Code so `status` is informative.
403
+ if (client === "claude-code" || client === "claude-code-cli") {
404
+ return (hasJsonMcpEntry(claudeCodeProjectConfig(cwd), name) ||
405
+ hasJsonMcpEntry(claudeCodeUserConfig(), name));
406
+ }
407
+ if (client === "cowork")
408
+ return hasJsonMcpEntry(coworkConfigPath(), name);
409
+ return codexHasBlock(codexConfigPath(), name);
410
+ }
411
+ // ---------------------------------------------------------------------------
412
+ // Subcommands
413
+ // ---------------------------------------------------------------------------
414
+ async function cmdServe(p) {
415
+ await runMCPStdio({
416
+ appId: p.app,
417
+ port: p.port,
418
+ standalone: p.standalone,
419
+ });
420
+ }
421
+ async function cmdInstall(p) {
422
+ const client = (p.client ?? "").toLowerCase();
423
+ if (!CLIENTS.includes(client)) {
424
+ logErr(`Usage: agent-native mcp install --client ${CLIENTS.join("|")} ` +
425
+ `[--app <id>] [--scope user|project]`);
426
+ process.exit(1);
427
+ }
428
+ const cwd = process.cwd();
429
+ // Resolve which app this entry targets (default = workspace default app).
430
+ let appId = p.app;
431
+ if (!appId) {
432
+ try {
433
+ const resolved = await resolveLocalAppOrigin({ cwd });
434
+ appId = resolved.appId;
435
+ }
436
+ catch {
437
+ appId = "app";
438
+ }
439
+ }
440
+ const serverName = serverNameFor(appId);
441
+ const hostedUrl = detectHostedUrl(cwd);
442
+ const ownerEmail = process.env.AGENT_NATIVE_OWNER_EMAIL;
443
+ let token;
444
+ if (hostedUrl) {
445
+ token = await mintHostedJwt(cwd);
446
+ logOut(`Detected hosted deployment: ${hostedUrl}`);
447
+ }
448
+ else {
449
+ const t = ensureLocalToken(cwd, false);
450
+ token = t.token;
451
+ logOut(t.created
452
+ ? `Provisioned ACCESS_TOKEN in ${t.file}`
453
+ : `Reusing existing ACCESS_TOKEN from ${t.file}`);
454
+ }
455
+ const inputs = {
456
+ serverName,
457
+ appId: appId,
458
+ token,
459
+ ownerEmail,
460
+ hostedUrl,
461
+ standalone: p.standalone,
462
+ };
463
+ const file = installForClient(client, inputs, cwd, p.scope);
464
+ logOut(`Installed "${serverName}" for ${client} → ${file}`);
465
+ logOut(hostedUrl
466
+ ? ` Mode: http (${hostedUrl})`
467
+ : ` Mode: stdio (agent-native mcp serve --app ${appId}${p.standalone ? " --standalone" : ""})`);
468
+ logOut(` Restart ${client} to pick up the new MCP server.`);
469
+ }
470
+ function cmdUninstall(p) {
471
+ const client = (p.client ?? "").toLowerCase();
472
+ if (!CLIENTS.includes(client)) {
473
+ logErr(`Usage: agent-native mcp uninstall --client ${CLIENTS.join("|")} ` +
474
+ `[--app <id>]`);
475
+ process.exit(1);
476
+ }
477
+ const cwd = process.cwd();
478
+ const appId = p.app ?? "app";
479
+ const { file, removed } = uninstallForClient(client, appId, cwd, p.scope);
480
+ logOut(removed
481
+ ? `Removed "${serverNameFor(appId)}" from ${client} → ${file}`
482
+ : `No "${serverNameFor(appId)}" entry found for ${client} (${file}) — nothing to do.`);
483
+ }
484
+ async function cmdStatus() {
485
+ const cwd = process.cwd();
486
+ let appId = "app";
487
+ let origin = "(app not running)";
488
+ let port;
489
+ try {
490
+ const resolved = await resolveLocalAppOrigin({ cwd });
491
+ appId = resolved.appId;
492
+ origin = resolved.origin;
493
+ const ws = await resolveWorkspace(cwd);
494
+ port = ws.apps.find((a) => a.id === appId)?.port;
495
+ }
496
+ catch (err) {
497
+ logErr(` Could not resolve app: ${err?.message ?? err}`);
498
+ }
499
+ const hostedUrl = detectHostedUrl(cwd);
500
+ const baseDir = envBaseDir(cwd);
501
+ const envContent = readEnvFile(path.join(baseDir, ".env.local")) +
502
+ "\n" +
503
+ readEnvFile(path.join(baseDir, ".env"));
504
+ const hasToken = !!getEnvValue(envContent, "ACCESS_TOKEN");
505
+ const hasA2A = !!process.env.A2A_SECRET || !!getEnvValue(envContent, "A2A_SECRET");
506
+ logOut(`Agent-Native MCP status`);
507
+ logOut(` App: ${appId}`);
508
+ logOut(hostedUrl
509
+ ? ` MCP URL: ${hostedUrl} (hosted)`
510
+ : ` MCP URL: ${origin}/_agent-native/mcp${port ? ` (port ${port})` : ""}`);
511
+ logOut(` ACCESS_TOKEN: ${hasToken ? "set" : "not set"} (.env)`);
512
+ logOut(` A2A_SECRET: ${hasA2A ? "set" : "not set"}`);
513
+ logOut(` Clients:`);
514
+ for (const client of CLIENTS) {
515
+ const present = clientHasEntry(client, appId, cwd);
516
+ logOut(` ${client.padEnd(18)} ${present ? "configured" : "—"}`);
517
+ }
518
+ }
519
+ function cmdToken(p) {
520
+ const cwd = process.cwd();
521
+ const t = ensureLocalToken(cwd, p.rotate);
522
+ logOut(p.rotate
523
+ ? `Rotated ACCESS_TOKEN in ${t.file}`
524
+ : t.created
525
+ ? `Provisioned ACCESS_TOKEN in ${t.file}`
526
+ : `ACCESS_TOKEN (${t.file}):`);
527
+ logOut(t.token);
528
+ if (p.rotate) {
529
+ logOut(` Re-run \`agent-native mcp install --client <c>\` so client configs ` +
530
+ `pick up the new token.`);
531
+ }
532
+ }
533
+ const HELP = `agent-native mcp — connect external coding agents over MCP
534
+
535
+ Usage:
536
+ agent-native mcp serve [--app <id>] [--port <n>] [--standalone]
537
+ Run the MCP stdio transport (what client configs spawn).
538
+ Default: proxy to the running local app; --standalone builds from disk.
539
+
540
+ agent-native mcp install --client <c> [--app <id>] [--scope user|project]
541
+ Provision a token and write the client's MCP config (idempotent).
542
+ Clients: claude-code, claude-code-cli, codex, cowork
543
+
544
+ agent-native mcp uninstall --client <c> [--app <id>]
545
+ Remove the named MCP entry from a client's config (idempotent).
546
+
547
+ agent-native mcp status
548
+ Show resolved MCP URL/port, token state, and per-client entries.
549
+
550
+ agent-native mcp token [--rotate]
551
+ Print (or rotate) the local ACCESS_TOKEN in the workspace .env.`;
552
+ export async function runMcp(args) {
553
+ const p = parseArgs(args);
554
+ const sub = p._[0];
555
+ switch (sub) {
556
+ case "serve":
557
+ await cmdServe(p);
558
+ return;
559
+ case "install":
560
+ await cmdInstall(p);
561
+ return;
562
+ case "uninstall":
563
+ cmdUninstall(p);
564
+ return;
565
+ case "status":
566
+ await cmdStatus();
567
+ return;
568
+ case "token":
569
+ cmdToken(p);
570
+ return;
571
+ case undefined:
572
+ case "--help":
573
+ case "-h":
574
+ case "help":
575
+ logOut(HELP);
576
+ return;
577
+ default:
578
+ logErr(`Unknown mcp subcommand: ${sub}`);
579
+ logOut(HELP);
580
+ process.exit(1);
581
+ }
582
+ }
583
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/cli/mcp.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `agent-native mcp <subcommand>` — connect external coding agents (Claude\n * Code desktop & CLI, Claude Cowork, Codex) to this agent-native app/workspace\n * over MCP.\n *\n * serve Run the MCP stdio transport (this is what client configs spawn).\n * install Provision a token + write the client's MCP config idempotently.\n * uninstall Remove the named entry from a client's MCP config.\n * status Print resolved MCP URL/port, token state, and per-client entries.\n * token Print or rotate the local ACCESS_TOKEN in the workspace .env.\n *\n * Node-only CLI module. Hand-rolled `.env` upsert + minimal TOML block merge\n * keep this dependency-free (no new npm deps).\n */\n\nimport crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nimport { runMCPStdio } from \"../mcp/stdio.js\";\nimport {\n findWorkspaceRoot,\n resolveLocalAppOrigin,\n resolveWorkspace,\n} from \"../mcp/workspace-resolve.js\";\n\nconst SERVER_NAME_PREFIX = \"agent-native\";\n\ntype ClientId = \"claude-code\" | \"claude-code-cli\" | \"codex\" | \"cowork\";\nconst CLIENTS: ClientId[] = [\n \"claude-code\",\n \"claude-code-cli\",\n \"codex\",\n \"cowork\",\n];\n\ninterface ParsedArgs {\n _: string[];\n client?: string;\n app?: string;\n port?: number;\n scope?: string;\n standalone: boolean;\n rotate: boolean;\n}\n\nfunction parseArgs(argv: string[]): ParsedArgs {\n const out: ParsedArgs = { _: [], standalone: false, rotate: false };\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i];\n const eat = (flag: string): string | undefined => {\n if (a === flag) return argv[++i];\n if (a.startsWith(`${flag}=`)) return a.slice(flag.length + 1);\n return undefined;\n };\n let v: string | undefined;\n if ((v = eat(\"--client\")) !== undefined) out.client = v;\n else if ((v = eat(\"--app\")) !== undefined) out.app = v;\n else if ((v = eat(\"--port\")) !== undefined) out.port = Number(v);\n else if ((v = eat(\"--scope\")) !== undefined) out.scope = v;\n else if (a === \"--standalone\") out.standalone = true;\n else if (a === \"--rotate\") out.rotate = true;\n else if (!a.startsWith(\"-\")) out._.push(a);\n }\n return out;\n}\n\nfunction logErr(msg: string): void {\n process.stderr.write(`${msg}\\n`);\n}\nfunction logOut(msg: string): void {\n process.stdout.write(`${msg}\\n`);\n}\n\n// ---------------------------------------------------------------------------\n// .env token provisioning (local dev) — hand-rolled idempotent upsert\n// ---------------------------------------------------------------------------\n\n/** Workspace root (or cwd for a standalone app) — where .env lives. */\nfunction envBaseDir(cwd = process.cwd()): string {\n return findWorkspaceRoot(cwd) ?? path.resolve(cwd);\n}\n\n/** Prefer .env.local, else .env. Returns the path we should write to. */\nfunction envFilePath(baseDir: string): string {\n const local = path.join(baseDir, \".env.local\");\n if (fs.existsSync(local)) return local;\n return path.join(baseDir, \".env\");\n}\n\nfunction readEnvFile(file: string): string {\n try {\n return fs.readFileSync(file, \"utf-8\");\n } catch {\n return \"\";\n }\n}\n\n/** Read a single key from a dotenv-format string (last assignment wins). */\nfunction getEnvValue(content: string, key: string): string | undefined {\n let found: string | undefined;\n for (const line of content.split(/\\r?\\n/)) {\n const m = line.match(/^\\s*([A-Z0-9_]+)\\s*=\\s*(.*)\\s*$/i);\n if (m && m[1] === key) {\n found = m[2].replace(/^[\"']|[\"']$/g, \"\");\n }\n }\n return found;\n}\n\n/**\n * Idempotently set `key=value` in the dotenv file. If the key already exists\n * we leave it untouched unless `force` is set (used by `token --rotate`).\n * Never clobbers an existing token implicitly.\n */\nfunction upsertEnv(\n file: string,\n key: string,\n value: string,\n force = false,\n): { changed: boolean; value: string } {\n const content = readEnvFile(file);\n const existing = getEnvValue(content, key);\n if (existing && !force) return { changed: false, value: existing };\n\n const line = `${key}=${value}`;\n let next: string;\n if (new RegExp(`^\\\\s*${key}\\\\s*=`, \"m\").test(content)) {\n next = content.replace(new RegExp(`^\\\\s*${key}\\\\s*=.*$`, \"m\"), line);\n } else {\n next =\n content.length === 0\n ? `${line}\\n`\n : `${content.replace(/\\n*$/, \"\")}\\n${line}\\n`;\n }\n fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.writeFileSync(file, next, \"utf-8\");\n return { changed: true, value };\n}\n\nfunction generateToken(): string {\n return crypto.randomBytes(24).toString(\"base64url\");\n}\n\n/**\n * Ensure a local ACCESS_TOKEN exists in the workspace .env and return it.\n * Existing tokens are reused (never clobbered). Set `rotate` to replace it.\n */\nfunction ensureLocalToken(\n cwd: string,\n rotate = false,\n): { token: string; file: string; created: boolean } {\n const baseDir = envBaseDir(cwd);\n const file = envFilePath(baseDir);\n const content = readEnvFile(file);\n const existing = getEnvValue(content, \"ACCESS_TOKEN\");\n if (existing && !rotate) {\n return { token: existing, file, created: false };\n }\n const token = generateToken();\n upsertEnv(file, \"ACCESS_TOKEN\", token, true);\n return { token, file, created: true };\n}\n\n// ---------------------------------------------------------------------------\n// Hosted vs local detection\n// ---------------------------------------------------------------------------\n\n/**\n * Detect a hosted deployment URL. When the workspace .env points at a hosted\n * origin (APP_URL / BETTER_AUTH_URL with a non-localhost host) we write an\n * `http` client entry pointing at `<origin>/_agent-native/mcp` with a JWT\n * bearer instead of a stdio entry.\n */\nfunction detectHostedUrl(cwd: string): string | undefined {\n const baseDir = envBaseDir(cwd);\n const content =\n readEnvFile(path.join(baseDir, \".env.local\")) +\n \"\\n\" +\n readEnvFile(path.join(baseDir, \".env\"));\n for (const key of [\"AGENT_NATIVE_MCP_URL\", \"APP_URL\", \"BETTER_AUTH_URL\"]) {\n const v = getEnvValue(content, key);\n if (!v) continue;\n try {\n const u = new URL(v);\n if (!/^(localhost|127\\.0\\.0\\.1|\\[::1\\])$/.test(u.hostname)) {\n return `${u.origin}/_agent-native/mcp`;\n }\n } catch {\n // not a URL — skip\n }\n }\n return undefined;\n}\n\nasync function mintHostedJwt(cwd: string): Promise<string | undefined> {\n // Reuse the existing A2A signer — do not reinvent JWT minting.\n const owner =\n process.env.AGENT_NATIVE_OWNER_EMAIL ||\n process.env.OWNER_EMAIL ||\n \"owner@localhost\";\n if (!process.env.A2A_SECRET) {\n const baseDir = envBaseDir(cwd);\n const content =\n readEnvFile(path.join(baseDir, \".env.local\")) +\n \"\\n\" +\n readEnvFile(path.join(baseDir, \".env\"));\n const secret = getEnvValue(content, \"A2A_SECRET\");\n if (secret) process.env.A2A_SECRET = secret;\n }\n try {\n const { signA2AToken } = await import(\"../a2a/client.js\");\n return await signA2AToken(owner, undefined, undefined, {\n preferGlobalSecret: true,\n expiresIn: \"30d\",\n });\n } catch (err: any) {\n logErr(\n ` Could not mint a hosted JWT (${err?.message ?? err}). ` +\n `Set A2A_SECRET in your workspace .env, or use the local stdio entry.`,\n );\n return undefined;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Client config file locations + writers\n// ---------------------------------------------------------------------------\n\n/**\n * Cowork consumes MCP exactly like Claude Code (same JSON server-entry\n * shape). The exact on-disk config path for Cowork may differ across builds —\n * this is the best-known location. **Confirm before relying on it in\n * production.** It is validated against the Claude Code JSON format below.\n *\n * Resolved lazily (not as a module-level constant) so `os.homedir()` reflects\n * the current `$HOME` rather than the value at module-load time.\n */\nfunction coworkConfigPath(): string {\n return path.join(os.homedir(), \".cowork\", \"mcp.json\");\n}\n\nfunction claudeCodeProjectConfig(cwd: string): string {\n return path.join(envBaseDir(cwd), \".mcp.json\");\n}\nfunction claudeCodeUserConfig(): string {\n return path.join(os.homedir(), \".claude.json\");\n}\nfunction codexConfigPath(): string {\n return path.join(os.homedir(), \".codex\", \"config.toml\");\n}\n\ninterface ServerEntryInputs {\n serverName: string;\n appId: string;\n token?: string;\n ownerEmail?: string;\n hostedUrl?: string;\n standalone: boolean;\n}\n\n/** The stdio (or http) server entry — shared by Claude Code & Cowork JSON. */\nfunction buildJsonServerEntry(i: ServerEntryInputs): Record<string, unknown> {\n if (i.hostedUrl) {\n return {\n type: \"http\",\n url: i.hostedUrl,\n ...(i.token ? { headers: { Authorization: `Bearer ${i.token}` } } : {}),\n };\n }\n const args = [\"mcp\", \"serve\"];\n if (i.appId) args.push(\"--app\", i.appId);\n if (i.standalone) args.push(\"--standalone\");\n const env: Record<string, string> = {};\n if (i.token) env.ACCESS_TOKEN = i.token;\n if (i.ownerEmail) env.AGENT_NATIVE_OWNER_EMAIL = i.ownerEmail;\n return {\n command: \"agent-native\",\n args,\n ...(Object.keys(env).length ? { env } : {}),\n };\n}\n\nfunction readJsonFile(file: string): Record<string, any> {\n try {\n const raw = fs.readFileSync(file, \"utf-8\");\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === \"object\" ? parsed : {};\n } catch {\n return {};\n }\n}\n\n/** Idempotently write `mcpServers[name] = entry` into a JSON config file. */\nfunction 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 fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.writeFileSync(file, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n}\n\nfunction hasJsonMcpEntry(file: string, name: string): boolean {\n const config = readJsonFile(file);\n return !!config?.mcpServers && name in config.mcpServers;\n}\n\n// --- Codex TOML (hand-rolled minimal block merge, no new dep) -------------\n\nfunction tomlQuote(s: string): string {\n return `\"${s.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n}\n\nfunction buildCodexBlock(name: string, i: ServerEntryInputs): string {\n const lines: string[] = [`[mcp_servers.${name}]`];\n const args = [\"mcp\", \"serve\"];\n if (i.appId) args.push(\"--app\", i.appId);\n if (i.standalone) args.push(\"--standalone\");\n lines.push(`command = \"agent-native\"`);\n lines.push(`args = [${args.map(tomlQuote).join(\", \")}]`);\n const env: Record<string, string> = {};\n if (i.token) env.ACCESS_TOKEN = i.token;\n if (i.ownerEmail) env.AGENT_NATIVE_OWNER_EMAIL = i.ownerEmail;\n if (Object.keys(env).length) {\n const inline = Object.entries(env)\n .map(([k, v]) => `${k} = ${tomlQuote(v)}`)\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. We treat a block as the header line plus\n * every following line until the next top-level `[` table header or EOF.\n */\nfunction 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 header = `[mcp_servers.${name}]`;\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 (line.trim() === header) {\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 fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.writeFileSync(file, next, \"utf-8\");\n}\n\nfunction codexHasBlock(file: string, name: string): boolean {\n try {\n const content = fs.readFileSync(file, \"utf-8\");\n return new RegExp(`^\\\\s*\\\\[mcp_servers\\\\.${name}\\\\]\\\\s*$`, \"m\").test(\n content,\n );\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Per-client install/uninstall/status\n// ---------------------------------------------------------------------------\n\nfunction configPathFor(\n client: ClientId,\n cwd: 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(cwd);\n case \"cowork\":\n return coworkConfigPath();\n case \"codex\":\n return codexConfigPath();\n }\n}\n\nfunction serverNameFor(appId: string): string {\n return `${SERVER_NAME_PREFIX}-${appId}`;\n}\n\nfunction installForClient(\n client: ClientId,\n inputs: ServerEntryInputs,\n cwd: string,\n scope: string | undefined,\n): string {\n const name = inputs.serverName;\n const file = configPathFor(client, cwd, scope);\n if (client === \"codex\") {\n writeCodexBlock(file, name, buildCodexBlock(name, inputs));\n } else {\n writeJsonMcpEntry(file, name, buildJsonServerEntry(inputs));\n }\n return file;\n}\n\nfunction uninstallForClient(\n client: ClientId,\n appId: string,\n cwd: string,\n scope: string | undefined,\n): { file: string; removed: boolean } {\n const name = serverNameFor(appId);\n const file = configPathFor(client, cwd, scope);\n if (client === \"codex\") {\n const had = codexHasBlock(file, name);\n if (had) writeCodexBlock(file, name, null);\n return { file, removed: had };\n }\n const had = hasJsonMcpEntry(file, name);\n if (had) writeJsonMcpEntry(file, name, null);\n return { file, removed: had };\n}\n\nfunction clientHasEntry(client: ClientId, appId: string, cwd: string): boolean {\n const name = serverNameFor(appId);\n // Check both scopes for Claude Code so `status` is informative.\n if (client === \"claude-code\" || client === \"claude-code-cli\") {\n return (\n hasJsonMcpEntry(claudeCodeProjectConfig(cwd), name) ||\n hasJsonMcpEntry(claudeCodeUserConfig(), name)\n );\n }\n if (client === \"cowork\") return hasJsonMcpEntry(coworkConfigPath(), name);\n return codexHasBlock(codexConfigPath(), name);\n}\n\n// ---------------------------------------------------------------------------\n// Subcommands\n// ---------------------------------------------------------------------------\n\nasync function cmdServe(p: ParsedArgs): Promise<void> {\n await runMCPStdio({\n appId: p.app,\n port: p.port,\n standalone: p.standalone,\n });\n}\n\nasync function cmdInstall(p: ParsedArgs): Promise<void> {\n const client = (p.client ?? \"\").toLowerCase() as ClientId;\n if (!CLIENTS.includes(client)) {\n logErr(\n `Usage: agent-native mcp install --client ${CLIENTS.join(\"|\")} ` +\n `[--app <id>] [--scope user|project]`,\n );\n process.exit(1);\n }\n const cwd = process.cwd();\n\n // Resolve which app this entry targets (default = workspace default app).\n let appId = p.app;\n if (!appId) {\n try {\n const resolved = await resolveLocalAppOrigin({ cwd });\n appId = resolved.appId;\n } catch {\n appId = \"app\";\n }\n }\n const serverName = serverNameFor(appId);\n\n const hostedUrl = detectHostedUrl(cwd);\n const ownerEmail = process.env.AGENT_NATIVE_OWNER_EMAIL;\n\n let token: string | undefined;\n if (hostedUrl) {\n token = await mintHostedJwt(cwd);\n logOut(`Detected hosted deployment: ${hostedUrl}`);\n } else {\n const t = ensureLocalToken(cwd, false);\n token = t.token;\n logOut(\n t.created\n ? `Provisioned ACCESS_TOKEN in ${t.file}`\n : `Reusing existing ACCESS_TOKEN from ${t.file}`,\n );\n }\n\n const inputs: ServerEntryInputs = {\n serverName,\n appId: appId!,\n token,\n ownerEmail,\n hostedUrl,\n standalone: p.standalone,\n };\n\n const file = installForClient(client, inputs, cwd, p.scope);\n logOut(`Installed \"${serverName}\" for ${client} → ${file}`);\n logOut(\n hostedUrl\n ? ` Mode: http (${hostedUrl})`\n : ` Mode: stdio (agent-native mcp serve --app ${appId}${\n p.standalone ? \" --standalone\" : \"\"\n })`,\n );\n logOut(` Restart ${client} to pick up the new MCP server.`);\n}\n\nfunction cmdUninstall(p: ParsedArgs): void {\n const client = (p.client ?? \"\").toLowerCase() as ClientId;\n if (!CLIENTS.includes(client)) {\n logErr(\n `Usage: agent-native mcp uninstall --client ${CLIENTS.join(\"|\")} ` +\n `[--app <id>]`,\n );\n process.exit(1);\n }\n const cwd = process.cwd();\n const appId = p.app ?? \"app\";\n const { file, removed } = uninstallForClient(client, appId, cwd, p.scope);\n logOut(\n removed\n ? `Removed \"${serverNameFor(appId)}\" from ${client} → ${file}`\n : `No \"${serverNameFor(appId)}\" entry found for ${client} (${file}) — nothing to do.`,\n );\n}\n\nasync function cmdStatus(): Promise<void> {\n const cwd = process.cwd();\n let appId = \"app\";\n let origin = \"(app not running)\";\n let port: number | undefined;\n try {\n const resolved = await resolveLocalAppOrigin({ cwd });\n appId = resolved.appId;\n origin = resolved.origin;\n const ws = await resolveWorkspace(cwd);\n port = ws.apps.find((a) => a.id === appId)?.port;\n } catch (err: any) {\n logErr(` Could not resolve app: ${err?.message ?? err}`);\n }\n\n const hostedUrl = detectHostedUrl(cwd);\n const baseDir = envBaseDir(cwd);\n const envContent =\n readEnvFile(path.join(baseDir, \".env.local\")) +\n \"\\n\" +\n readEnvFile(path.join(baseDir, \".env\"));\n const hasToken = !!getEnvValue(envContent, \"ACCESS_TOKEN\");\n const hasA2A =\n !!process.env.A2A_SECRET || !!getEnvValue(envContent, \"A2A_SECRET\");\n\n logOut(`Agent-Native MCP status`);\n logOut(` App: ${appId}`);\n logOut(\n hostedUrl\n ? ` MCP URL: ${hostedUrl} (hosted)`\n : ` MCP URL: ${origin}/_agent-native/mcp${\n port ? ` (port ${port})` : \"\"\n }`,\n );\n logOut(` ACCESS_TOKEN: ${hasToken ? \"set\" : \"not set\"} (.env)`);\n logOut(` A2A_SECRET: ${hasA2A ? \"set\" : \"not set\"}`);\n logOut(` Clients:`);\n for (const client of CLIENTS) {\n const present = clientHasEntry(client, appId, cwd);\n logOut(` ${client.padEnd(18)} ${present ? \"configured\" : \"—\"}`);\n }\n}\n\nfunction cmdToken(p: ParsedArgs): void {\n const cwd = process.cwd();\n const t = ensureLocalToken(cwd, p.rotate);\n logOut(\n p.rotate\n ? `Rotated ACCESS_TOKEN in ${t.file}`\n : t.created\n ? `Provisioned ACCESS_TOKEN in ${t.file}`\n : `ACCESS_TOKEN (${t.file}):`,\n );\n logOut(t.token);\n if (p.rotate) {\n logOut(\n ` Re-run \\`agent-native mcp install --client <c>\\` so client configs ` +\n `pick up the new token.`,\n );\n }\n}\n\nconst HELP = `agent-native mcp — connect external coding agents over MCP\n\nUsage:\n agent-native mcp serve [--app <id>] [--port <n>] [--standalone]\n Run the MCP stdio transport (what client configs spawn).\n Default: proxy to the running local app; --standalone builds from disk.\n\n agent-native mcp install --client <c> [--app <id>] [--scope user|project]\n Provision a token and write the client's MCP config (idempotent).\n Clients: claude-code, claude-code-cli, codex, cowork\n\n agent-native mcp uninstall --client <c> [--app <id>]\n Remove the named MCP entry from a client's config (idempotent).\n\n agent-native mcp status\n Show resolved MCP URL/port, token state, and per-client entries.\n\n agent-native mcp token [--rotate]\n Print (or rotate) the local ACCESS_TOKEN in the workspace .env.`;\n\nexport async function runMcp(args: string[]): Promise<void> {\n const p = parseArgs(args);\n const sub = p._[0];\n\n switch (sub) {\n case \"serve\":\n await cmdServe(p);\n return;\n case \"install\":\n await cmdInstall(p);\n return;\n case \"uninstall\":\n cmdUninstall(p);\n return;\n case \"status\":\n await cmdStatus();\n return;\n case \"token\":\n cmdToken(p);\n return;\n case undefined:\n case \"--help\":\n case \"-h\":\n case \"help\":\n logOut(HELP);\n return;\n default:\n logErr(`Unknown mcp subcommand: ${sub}`);\n logOut(HELP);\n process.exit(1);\n }\n}\n"]}