argsbarg 3.3.13 → 3.4.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.
@@ -1,2 +1,4 @@
1
1
  - [x] --schema feature for ai agents
2
- - [x] opt-out install feature?
2
+ - [x] opt-out install feature?
3
+ - [x] outputSchema
4
+ - [x] mcpb bundle
package/CHANGELOG.md CHANGED
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.4.0] - 2026-06-22
11
+
12
+ ### Added
13
+
14
+ - **`hidden`** — boolean on commands and options; omitted from help listings, `docs schema` / `docs api`, shell completions, and MCP `tools/list`, but still parseable and invocable. Direct `-h` on a hidden command still works.
15
+ - **`mcp bundle`** — built-in subcommand when `mcpServer.enabled` (macOS-only v1). Runs `myapp mcp bundle` to pack `dist/<key>.mcpb` from `dist/<key>`. Bare `myapp mcp` still starts the stdio server.
16
+ - **`mcpServer.bundle`** — optional author, icon, and `longDescription` for MCP Bundle metadata.
17
+ - **Leaf `outputSchema`** — optional JSON Schema for structured stdout; exported in `docs schema`, `docs api`, skill `reference.md`, and MCP `tools/list` (stdout not validated at runtime yet). Legacy `mcpTool.outputSchema` still works.
18
+ - **MCP tool descriptions** — leaf `notes` are appended to `tools/list` descriptions (`{argsbarg:program}` resolved).
19
+
20
+ ## [3.3.14] - 2026-06-21
21
+
22
+ ### Changed
23
+
24
+ - **Generated notes** — deduplicated agent, docs, MCP, and completion help; each topic owns its guidance in one place.
25
+
10
26
  ## [3.3.13] - 2026-06-21
11
27
 
12
28
  ### Changed
@@ -329,7 +345,9 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
329
345
  - Migrate schemas: rename every `children` property to **`commands`**; move positional definitions to **`CliPositional`** objects on `positionals` and strip `positional` / `argMin` / `argMax` from flag definitions under `options` (flags only carry `name`, `description`, `kind`, and optional `shortName`).
330
346
  - Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
331
347
 
332
- [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.3.13...HEAD
348
+ [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.4.0...HEAD
349
+ [3.4.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.4.0
350
+ [3.3.14]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.14
333
351
  [3.3.13]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.13
334
352
  [3.3.12]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.12
335
353
  [3.3.11]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.11
@@ -35,7 +35,7 @@ myapp docs readme --save # write ./docs/readme.md
35
35
  myapp docs schema --save # write ./docs/schema.json
36
36
  ```
37
37
 
38
- When `docs` is enabled, top-level `myapp --help` includes a Notes line: `Agents: run \`myapp docs skill\` to learn how to use this app`.
38
+ When `docs` is enabled, top-level `myapp --help` points agents at `myapp docs skill`. The `docs skill` subcommand description recommends `install --skill` for a persisted bundle.
39
39
 
40
40
  ## Configuration
41
41
 
@@ -113,6 +113,25 @@ Many "MCP problems" are schema or handler gaps. Prefer these over escape hatches
113
113
  | `requiresEnv: [...]` | Runtime secrets; appended to MCP description and enforced at `tools/call` |
114
114
  | `description: "..."` | **Irreducible** MCP limitation (e.g. live tail / `--watch` cannot be streamed on the MCP wire yet) |
115
115
 
116
+ ### Structured stdout
117
+
118
+ On **leaf commands**, set `outputSchema` to a JSON Schema describing stdout when the handler emits JSON (typically with `--json`, or via MCP on the headless path):
119
+
120
+ ```typescript
121
+ {
122
+ key: "lookup",
123
+ description: "Resolve owner info.",
124
+ outputSchema: {
125
+ type: "object",
126
+ properties: { user: { type: "string" }, path: { type: "string" } },
127
+ required: ["user", "path"],
128
+ },
129
+ handler: (ctx) => { /* ... */ },
130
+ }
131
+ ```
132
+
133
+ Exported in `docs schema`, `docs api`, skill `reference.md`, and MCP `tools/list`. Not validated at runtime yet. Pair with `notes` for prose examples; do not duplicate the full schema in `notes`.
134
+
116
135
  Do **not** use `mcpTool.description` to paper over missing `--yes`, non-standard flag names, or handlers that only work interactively — fix those instead.
117
136
 
118
137
  If help text and MCP behavior match after your fixes, **omit `mcpTool` entirely**.
package/docs/mcp.md CHANGED
@@ -103,7 +103,7 @@ Tool names are derived from the command path, with each segment sanitized (non-a
103
103
 
104
104
  ### Tool descriptions
105
105
 
106
- Each tool’s `description` includes the human CLI path and the leaf’s help text, separated by an em dash. Tool arguments are defined in `inputSchema` (options and positionals with their descriptions). `mcpTool.requiresEnv` is appended as `[requires env: …]`.
106
+ Each tool’s `description` includes the human CLI path and the leaf’s help text, separated by an em dash. Leaf **`notes`** are appended after a blank line (with `{argsbarg:program}` resolved). Tool arguments are defined in `inputSchema` (options and positionals with their descriptions). `mcpTool.requiresEnv` is appended as `[requires env: …]` on auto-generated descriptions.
107
107
 
108
108
  | CLI path | MCP `description` (example) |
109
109
  | --- | --- |
@@ -138,6 +138,8 @@ mcpTool: {
138
138
  }
139
139
  ```
140
140
 
141
+ Set **`outputSchema` on the leaf** (not under `mcpTool`) — see [cli-program.md — Structured stdout](cli-program.md#structured-stdout).
142
+
141
143
  - **`description`** — when set, replaces the auto-generated `path — help` description entirely (no automatic `requiresEnv` suffix; mention vars in your text if needed).
142
144
  - **`requiresEnv`** — on auto-generated descriptions, appended as `[requires env: …]`. Enforced at `tools/call` time before the handler runs. Empty or unset env values count as missing.
143
145
 
@@ -276,7 +278,7 @@ mcpServer: {
276
278
  | `initialize` | Returns capabilities (`tools`, `resources`) and `serverInfo`. |
277
279
  | `notifications/initialized` | Acknowledged; no response (notification). |
278
280
  | `ping` | Returns `{}`. |
279
- | `tools/list` | Lists all tools with `name`, `description`, `inputSchema`. |
281
+ | `tools/list` | Lists all tools with `name`, `description`, `inputSchema`, and optional `outputSchema`. |
280
282
  | `tools/call` | Runs a leaf handler; params: `name`, `arguments` (object). |
281
283
  | `resources/list` | Lists schema + custom resources. |
282
284
  | `resources/read` | Returns resource body; params: `uri`. |
@@ -291,11 +293,29 @@ printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | bun
291
293
 
292
294
  You should get one JSON line on stdout with `result.capabilities` and `result.serverInfo`.
293
295
 
296
+ ## MCP Bundle (`mcp bundle`)
297
+
298
+ When `mcpServer.enabled` is true, **`mcp bundle`** packs a Claude Desktop **`.mcpb`** bundle (macOS-only v1):
299
+
300
+ ```bash
301
+ just build
302
+ ./dist/myapp mcp bundle
303
+ # → dist/myapp.mcpb
304
+ ```
305
+
306
+ Expects the compiled binary at **`dist/<program.key>`** and writes **`dist/<program.key>.mcpb`**. Manifest metadata is generated from your schema (`mcpServerId`, tools, `requiresEnv`). Optional pack-time fields live under **`mcpServer.bundle`** (`author`, `icon`, `longDescription`).
307
+
308
+ Bare **`myapp mcp`** still runs the stdio MCP server (unchanged for `install --mcp` and MCP hosts). Use **`install --mcp`** for Cursor / Claude Code JSON config.
309
+
310
+ ## Hidden commands and options
311
+
312
+ Set **`hidden: true`** on a command or option to omit it from help listings, `docs schema` / `docs api`, shell completions, and MCP `tools/list` / tool `inputSchema`. Hidden commands remain invocable; **`myapp hidden-cmd -h`** still works.
313
+
294
314
  ## Reserved names
295
315
 
296
316
  When MCP is enabled:
297
317
 
298
- - Do not declare a top-level command named **`completion`** — reserved for shell completions.
318
+ - Do not declare top-level commands named **`completion`** or **`mcp`** — reserved for platform builtins.
299
319
 
300
320
  Running `myapp mcp` without `mcpServer` on the root fails with an error (exit 1).
301
321
 
package/index.d.ts CHANGED
@@ -69,6 +69,8 @@ export declare enum CliFallbackMode {
69
69
  export interface CliOption {
70
70
  /** Option name (e.g., "name", "verbose"). */
71
71
  name: string;
72
+ /** When `true`, omit from help, schema, completions, and MCP tool inputSchema (still parseable). */
73
+ hidden?: boolean;
72
74
  /** Description shown in help. */
73
75
  description: string;
74
76
  /** Option kind: presence flag, string value, or number value. */
@@ -104,6 +106,18 @@ export interface CliPositional {
104
106
  */
105
107
  argMax?: number;
106
108
  }
109
+ /** Optional metadata for `mcp bundle` MCP Bundle output (program root `mcpServer.bundle` only). */
110
+ export interface CliMcpBundleConfig {
111
+ author?: {
112
+ name: string;
113
+ email?: string;
114
+ url?: string;
115
+ };
116
+ /** Repo-relative path to a PNG icon copied into the bundle. */
117
+ icon?: string;
118
+ /** Manifest `long_description` (defaults to program description). */
119
+ longDescription?: string;
120
+ }
107
121
  /**
108
122
  * Enables `myapp mcp` and MCP stdio server metadata (program root only).
109
123
  * Must include `enabled: true`; omit `mcpServer` entirely to disable MCP.
@@ -130,6 +144,8 @@ export interface CliMcpServerConfig {
130
144
  * URIs must be unique and must not equal schemaResourceUri.
131
145
  */
132
146
  resources?: CliMcpResource[];
147
+ /** Optional MCP Bundle (`.mcpb`) metadata for `mcp bundle`. */
148
+ bundle?: CliMcpBundleConfig;
133
149
  }
134
150
  /**
135
151
  * A custom MCP resource exposed under resources/list and resources/read.
@@ -163,6 +179,10 @@ export interface CliMcpToolConfig {
163
179
  * Empty string counts as absent.
164
180
  */
165
181
  requiresEnv?: string[];
182
+ /**
183
+ * @deprecated Set `outputSchema` on the leaf command instead.
184
+ */
185
+ outputSchema?: Record<string, unknown>;
166
186
  }
167
187
  /**
168
188
  * Opt-out and defaults for the `install` built-in (program root only).
@@ -222,6 +242,8 @@ export interface CliDocsConfig {
222
242
  export interface CliNodeBase {
223
243
  /** Program or command key (e.g., "myapp", "stat", "owner"). */
224
244
  key: string;
245
+ /** When `true`, omit from help listings, schema, completions, and MCP tools (still invocable). */
246
+ hidden?: boolean;
225
247
  /** Short description shown in help. */
226
248
  description: string;
227
249
  /** Additional notes shown in help (`{argsbarg:program}` → program key). */
@@ -237,6 +259,11 @@ export type CliLeaf = CliNodeBase & {
237
259
  handler: CliHandler;
238
260
  /** Positional argument definitions. */
239
261
  positionals?: CliPositional[];
262
+ /**
263
+ * JSON Schema for structured stdout (e.g. with `--json` or MCP when the handler emits JSON).
264
+ * Exported in `docs schema`, `docs api`, and MCP `tools/list`; not validated at runtime yet.
265
+ */
266
+ outputSchema?: Record<string, unknown>;
240
267
  /** Per-tool MCP exposure and metadata. */
241
268
  mcpTool?: CliMcpToolConfig;
242
269
  };
@@ -378,5 +405,25 @@ export declare function createGhVersionCheck(config: GhVersionCheckConfig): {
378
405
  };
379
406
  /** Shared `gh release view` fetcher for hooks and version-check refresh. */
380
407
  export declare function createGhFetchLatest(config: Pick<GhReleaseUpdateConfig, "repo" | "repoEnvHint">): () => Promise<string>;
408
+ /** Resolved paths for `mcp bundle`. */
409
+ export interface McpBundlePaths {
410
+ binaryPath: string;
411
+ outPath: string;
412
+ binaryName: string;
413
+ }
414
+ /** Default `dist/<key>` binary and `dist/<key>.mcpb` output under cwd. */
415
+ export declare function defaultMcpBundlePaths(program: CliProgram, cwd?: string): McpBundlePaths;
416
+ /** Generates MCPB `manifest.json` object from program schema and MCP tools. */
417
+ export declare function generateMcpManifest(program: CliProgram, binaryName: string): Record<string, unknown>;
418
+ export interface PackMcpBundleOpts {
419
+ cwd?: string;
420
+ binaryPath?: string;
421
+ outPath?: string;
422
+ }
423
+ /**
424
+ * Stages manifest + binary (+ optional icon) and writes a `.mcpb` ZIP.
425
+ * macOS-only v1; requires the compiled binary to exist.
426
+ */
427
+ export declare function packMcpBundle(program: CliProgram, opts?: PackMcpBundleOpts): string;
381
428
 
382
429
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argsbarg",
3
- "version": "3.3.13",
3
+ "version": "3.4.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "bun": ">=1.3"
@@ -57,9 +57,14 @@ describe("builtins help copy", () => {
57
57
  });
58
58
 
59
59
  test("mcp builtin description is user-facing", () => {
60
- const mcp = cliBuiltinMcpCommand();
60
+ const withDocs: CliProgram = {
61
+ ...fixture,
62
+ docs: { enabled: true, topics: { readme: { text: "# r\n" } } },
63
+ };
64
+ const mcp = cliBuiltinMcpCommand(withDocs);
61
65
  expect(mcp.description).toContain("MCP server");
62
- expect(mcp.notes).toContain('["mcp"]');
66
+ expect(mcp.notes).toContain("install --mcp --yes");
67
+ expect(mcp.notes).toContain("docs mcp");
63
68
  });
64
69
  });
65
70
 
@@ -87,7 +92,8 @@ describe("presentation root", () => {
87
92
  docs: { enabled: true, topics: { readme: { text: "# r\n" } } },
88
93
  };
89
94
  const root = cliPresentationRoot(withDocs);
90
- expect(root.notes).toContain("Agents: run `myapp docs skill` to learn how to use this app");
95
+ expect(root.notes).toContain("For AI agents: `myapp docs skill`.");
96
+ expect(root.notes).not.toContain("install --skill");
91
97
  });
92
98
  });
93
99
 
@@ -1,10 +1,13 @@
1
- import { type CliLeaf, type CliNode, type CliRouter } from "../types.ts";
1
+ import { resolveCapabilities } from "../capabilities.ts";
2
+ import { type CliLeaf, type CliProgram, type CliRouter } from "../types.ts";
2
3
 
3
4
  /**
4
5
  * Builds the static `completion` / `bash` / `zsh` / `fish` command subtree (merged into the program root at runtime).
5
6
  */
6
- export function cliBuiltinCompletionGroup(appName: string): CliRouter {
7
- return {
7
+ export function cliBuiltinCompletionGroup(program: CliProgram): CliRouter {
8
+ const appName = program.key;
9
+ const caps = resolveCapabilities(program);
10
+ const router: CliRouter = {
8
11
  key: "completion",
9
12
  description: "Generate the autocompletion script for shells.",
10
13
  commands: [
@@ -12,15 +15,10 @@ export function cliBuiltinCompletionGroup(appName: string): CliRouter {
12
15
  key: "bash",
13
16
  description: "Print a bash tab-completion script.",
14
17
  notes:
15
- "Output is the whole script.\n" +
16
- "Pipe it to a file, or feed it straight into your shell.\n\n" +
17
- "To keep it across restarts, save it and source that file from ~/.bashrc.\n\n" +
18
- "For example:\n\n" +
19
- `echo 'eval \"$(${appName} completion bash)\"' >> ~/.bashrc\n` +
20
- `\nor\n` +
18
+ "Manual install:\n\n" +
21
19
  ` ${appName} completion bash > ~/.bash_completion.d/${appName}\n` +
22
20
  ` echo 'source ~/.bash_completion.d/${appName}' >> ~/.bashrc\n\n` +
23
- "To try it only in this session (nothing written to disk):\n" +
21
+ "Try this session only:\n\n" +
24
22
  ` source <(${appName} completion bash)`,
25
23
  handler: () => {},
26
24
  },
@@ -28,23 +26,26 @@ export function cliBuiltinCompletionGroup(appName: string): CliRouter {
28
26
  key: "zsh",
29
27
  description: "Print a zsh tab-completion script.",
30
28
  notes:
31
- "Output is the whole script.\n\n" +
32
- `fpath setup: ${appName} completion zsh > ~/.zsh/completions/_${appName}\n\n` +
33
- `source setup: echo 'eval \"$(${appName} completion zsh)\"' >> ~/.zshrc\n\n` +
34
- "To try it only in this session (nothing written to disk):\n" +
35
- ` eval \"$(${appName} completion zsh)\"`,
29
+ "Manual install:\n\n" +
30
+ ` ${appName} completion zsh > ~/.zsh/completions/_${appName}\n\n` +
31
+ "Ensure ~/.zsh/completions is on your fpath, then restart zsh.\n\n" +
32
+ "Try this session only:\n\n" +
33
+ ` eval "$(${appName} completion zsh)"`,
36
34
  handler: () => {},
37
35
  },
38
36
  {
39
37
  key: "fish",
40
38
  description: "Print a fish tab-completion script.",
41
39
  notes:
42
- "Output is the whole script.\n\n" +
43
- "Install:\n" +
40
+ "Manual install:\n\n" +
44
41
  ` ${appName} completion fish > ~/.config/fish/completions/${appName}.fish\n\n` +
45
42
  "Fish loads completions from that directory automatically.",
46
43
  handler: () => {},
47
44
  },
48
45
  ],
49
46
  };
47
+ if (caps.install) {
48
+ router.notes = `Install for all shells:\n\n ${appName} install --completions --yes`;
49
+ }
50
+ return router;
50
51
  }
@@ -9,6 +9,7 @@ import { cliBuiltinMcpCommand } from "./mcp.ts";
9
9
  import { cliBuiltinVersionCommand } from "./version.ts";
10
10
  import { cliBuiltinCompletionGroup as completionGroup } from "./completion-group.ts";
11
11
  import { cliPresentationRoot } from "./presentation.ts";
12
+ import { runMcpBundle } from "../mcp/bundle.ts";
12
13
  import { cliBuiltinDocsGroupIfEnabled } from "../docs/builtin.ts";
13
14
  import { cliMcpServeStdio } from "../mcp.ts";
14
15
  import { cliInstall } from "../install/index.ts";
@@ -72,12 +73,22 @@ export async function dispatchBuiltin(
72
73
  process.stderr.write("MCP is not enabled. Set mcpServer: { enabled: true } on the program root.\n");
73
74
  process.exit(1);
74
75
  }
75
- if (pr.path.length !== 1) {
76
- process.stderr.write("Unknown subcommand: mcp " + pr.path.slice(1).join(" ") + "\n");
77
- process.exit(1);
76
+ const sub = pr.path[1];
77
+ if (pr.path.length === 1 || sub === "serve") {
78
+ await cliMcpServeStdio(program);
79
+ process.exit(0);
78
80
  }
79
- await cliMcpServeStdio(program);
80
- process.exit(0);
81
+ if (pr.path.length === 2 && sub === "bundle") {
82
+ try {
83
+ runMcpBundle(program);
84
+ } catch (err) {
85
+ process.stderr.write(err instanceof Error ? err.message + "\n" : "mcp bundle failed.\n");
86
+ process.exit(1);
87
+ }
88
+ process.exit(0);
89
+ }
90
+ process.stderr.write("Unknown subcommand: mcp " + pr.path.slice(1).join(" ") + "\n");
91
+ process.exit(1);
81
92
  }
82
93
 
83
94
  if (pr.path[0] === "install") {
@@ -110,7 +121,7 @@ export function builtinInterceptRoot(
110
121
  parseRoot: {
111
122
  key: program.key,
112
123
  description: program.description,
113
- commands: [completionGroup(program.key)],
124
+ commands: [completionGroup(program)],
114
125
  },
115
126
  isLeafCompletionIntercept: true,
116
127
  };
@@ -132,7 +143,7 @@ export function builtinInterceptRoot(
132
143
  parseRoot: {
133
144
  key: program.key,
134
145
  description: program.description,
135
- commands: [cliBuiltinMcpCommand()],
146
+ commands: [cliBuiltinMcpCommand(program)],
136
147
  },
137
148
  isLeafCompletionIntercept: false,
138
149
  };
@@ -1,5 +1,7 @@
1
1
  import { type CliCapabilities, resolveCapabilities } from "../capabilities.ts";
2
- import type { CliFallbackMode, CliOption, CliPositional, CliProgram } from "../types.ts";
2
+ import type { CliFallbackMode, CliNode, CliOption, CliPositional, CliProgram } from "../types.ts";
3
+ import { isCliRouter } from "../types.ts";
4
+ import { visibleOptions } from "../hidden.ts";
3
5
  import { cliBuiltinCompletionGroup } from "./completion-group.ts";
4
6
  import { cliBuiltinInstallCommand } from "./install.ts";
5
7
  import { cliBuiltinMcpCommand } from "./mcp.ts";
@@ -11,6 +13,8 @@ export interface CliSchemaExport {
11
13
  key: string;
12
14
  description: string;
13
15
  notes?: string;
16
+ /** JSON Schema for structured stdout when set on the leaf `mcpTool`. */
17
+ outputSchema?: Record<string, unknown>;
14
18
  options?: CliOption[];
15
19
  fallbackCommand?: string;
16
20
  fallbackMode?: CliFallbackMode;
@@ -18,13 +22,11 @@ export interface CliSchemaExport {
18
22
  positionals?: CliPositional[];
19
23
  }
20
24
 
21
- function exportBuiltinNode(cmd: {
22
- key: string;
23
- description: string;
24
- notes?: string;
25
- options?: CliOption[];
26
- commands?: CliSchemaExport[];
27
- }): CliSchemaExport {
25
+ function exportBuiltinNode(cmd: CliNode): CliSchemaExport | null {
26
+ if (cmd.hidden) {
27
+ return null;
28
+ }
29
+
28
30
  const out: CliSchemaExport = {
29
31
  key: cmd.key,
30
32
  description: cmd.description,
@@ -32,11 +34,23 @@ function exportBuiltinNode(cmd: {
32
34
  if ((cmd.notes ?? "").length > 0) {
33
35
  out.notes = cmd.notes;
34
36
  }
35
- if ((cmd.options ?? []).length > 0) {
36
- out.options = cmd.options;
37
+ const options = visibleOptions(cmd.options);
38
+ if (options.length > 0) {
39
+ out.options = options;
37
40
  }
38
- if ((cmd.commands ?? []).length > 0) {
39
- out.commands = (cmd.commands ?? []).map((ch) => exportBuiltinNode(ch));
41
+ if (isCliRouter(cmd)) {
42
+ if (cmd.fallbackCommand !== undefined) {
43
+ out.fallbackCommand = cmd.fallbackCommand;
44
+ }
45
+ if (cmd.fallbackMode !== undefined) {
46
+ out.fallbackMode = cmd.fallbackMode;
47
+ }
48
+ const children = cmd.commands
49
+ .map((ch) => exportBuiltinNode(ch))
50
+ .filter((ch): ch is CliSchemaExport => ch !== null);
51
+ if (children.length > 0) {
52
+ out.commands = children;
53
+ }
40
54
  }
41
55
  return out;
42
56
  }
@@ -45,18 +59,18 @@ function exportBuiltinNode(cmd: {
45
59
  export function exportPresentationBuiltins(program: CliProgram, caps?: CliCapabilities): CliSchemaExport[] {
46
60
  const resolved = caps ?? resolveCapabilities(program);
47
61
  const builtins: CliSchemaExport[] = [
48
- exportBuiltinNode(cliBuiltinCompletionGroup(program.key)),
49
- exportBuiltinNode(cliBuiltinVersionCommand()),
62
+ exportBuiltinNode(cliBuiltinCompletionGroup(program))!,
63
+ exportBuiltinNode(cliBuiltinVersionCommand())!,
50
64
  ];
51
65
  if (resolved.install) {
52
- builtins.push(exportBuiltinNode(cliBuiltinInstallCommand(program)));
66
+ builtins.push(exportBuiltinNode(cliBuiltinInstallCommand(program))!);
53
67
  }
54
68
  const docsGroup = cliBuiltinDocsGroupIfEnabled(program);
55
69
  if (docsGroup) {
56
- builtins.push(exportBuiltinNode(docsGroup));
70
+ builtins.push(exportBuiltinNode(docsGroup)!);
57
71
  }
58
72
  if (resolved.mcp) {
59
- builtins.push(exportBuiltinNode(cliBuiltinMcpCommand()));
73
+ builtins.push(exportBuiltinNode(cliBuiltinMcpCommand(program))!);
60
74
  }
61
75
  return builtins;
62
76
  }
@@ -4,7 +4,7 @@ export { completionFishScript } from "./completion-fish.ts";
4
4
  export { cliBuiltinCompletionGroup } from "./completion-group.ts";
5
5
  export { cliBuiltinInstallCommand, installBuiltinOptions } from "./install.ts";
6
6
  export { cliBuiltinMcpCommand } from "./mcp.ts";
7
- export { cliPresentationRoot, presentationBuiltins } from "./presentation.ts";
7
+ export { cliParseRoot, cliPresentationRoot, parseBuiltins, presentationBuiltins } from "./presentation.ts";
8
8
  export { exportPresentationBuiltins, type CliSchemaExport } from "./export.ts";
9
9
  export { dispatchBuiltin, builtinInterceptRoot } from "./dispatch.ts";
10
10
  export { collectScopes, type ScopeRec } from "./scopes.ts";
@@ -117,7 +117,8 @@ export function cliBuiltinInstallCommand(root: CliProgram): CliLeaf {
117
117
  "Remove everything installed with --all:",
118
118
  ` ${app} install --uninstall --all --yes`,
119
119
  "",
120
- "Use --dry to preview changes, --json for machine-readable output.",
120
+ "Use --dry to preview changes without writing files.",
121
+ "Use --json for machine-readable output.",
121
122
  );
122
123
  return {
123
124
  key: "install",
@@ -1,13 +1,43 @@
1
- import { type CliLeaf } from "../types.ts";
1
+ import { resolveCapabilities } from "../capabilities.ts";
2
+ import { docsEnabled } from "../docs/resolve.ts";
3
+ import { type CliLeaf, type CliProgram, type CliRouter, CliFallbackMode } from "../types.ts";
2
4
 
3
- /** Presence options for the top-level `mcp` built-in (leaf). */
4
- export function cliBuiltinMcpCommand(): CliLeaf {
5
- return {
6
- key: "mcp",
5
+ /** Built-in `mcp` router: bare `myapp mcp` runs stdio (via hidden `serve` fallback); `mcp bundle` packs `.mcpb`. */
6
+ export function cliBuiltinMcpCommand(program: CliProgram): CliRouter {
7
+ const caps = resolveCapabilities(program);
8
+ const lines = [
9
+ "Stdio MCP server. Add to Cursor or Claude:",
10
+ "",
11
+ " command: {argsbarg:program}",
12
+ " args: mcp",
13
+ "",
14
+ ];
15
+ if (caps.install) {
16
+ lines.push("Or:", "", " {argsbarg:program} install --mcp --yes", "");
17
+ }
18
+ if (docsEnabled(program)) {
19
+ lines.push("Full setup guide: {argsbarg:program} docs mcp");
20
+ }
21
+
22
+ const serve: CliLeaf = {
23
+ key: "serve",
24
+ hidden: true,
7
25
  description: "Run as an MCP server over stdio for AI agents.",
8
- notes:
9
- "Configure MCP clients with `command` set to this program name and `args` set to `[\"mcp\"]`.\n\n" +
10
- "See docs/mcp.md for setup details.",
11
26
  handler: () => {},
12
27
  };
28
+
29
+ const bundle: CliLeaf = {
30
+ key: "bundle",
31
+ description: "Pack a Claude Desktop MCP Bundle (.mcpb) from dist/<key> (macOS-only v1).",
32
+ handler: () => {},
33
+ };
34
+
35
+ return {
36
+ key: "mcp",
37
+ description: "MCP server and bundle tools.",
38
+ notes: lines.join("\n"),
39
+ fallbackCommand: "serve",
40
+ fallbackMode: CliFallbackMode.MissingOnly,
41
+ commands: [serve, bundle],
42
+ };
13
43
  }