argsbarg 3.3.14 → 3.4.1

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.
package/src/mcp/tools.ts CHANGED
@@ -3,9 +3,11 @@ This module maps CliProgram leaf nodes to MCP tool definitions and converts
3
3
  flat JSON tool arguments into argv for cliInvoke.
4
4
  */
5
5
 
6
+ import { cliResolveNotes } from "../help.ts";
6
7
  import { collectOptionDefs } from "../parse.ts";
8
+ import { visibleOptions } from "../hidden.ts";
7
9
  import { cliSchemaJson } from "../schema.ts";
8
- import { CliProgram, CliLeaf, CliNode, CliOption, CliOptionKind, CliPositional, isCliLeaf, isCliRouter } from "../types.ts";
10
+ import { CliProgram, CliLeaf, CliNode, CliOption, CliOptionKind, CliPositional, isCliLeaf, isCliRouter, leafOutputSchema } from "../types.ts";
9
11
 
10
12
  /** Default URI pattern for the CLI schema MCP resource (`<mcpId>://schema`). */
11
13
  export function defaultMcpSchemaUri(mcpId: string): string {
@@ -34,6 +36,8 @@ export interface McpToolDef {
34
36
  leaf: CliLeaf;
35
37
  /** JSON Schema for tools/call arguments. */
36
38
  inputSchema: Record<string, unknown>;
39
+ /** JSON Schema for structured tool results when set on the leaf `mcpTool`. */
40
+ outputSchema?: Record<string, unknown>;
37
41
  }
38
42
 
39
43
  /** Builds MCP tool description: "{cli path} — {description}". */
@@ -80,7 +84,7 @@ function buildInputSchema(root: CliProgram, path: string[], leaf: CliLeaf): Reco
80
84
  const properties: Record<string, unknown> = {};
81
85
  const required: string[] = [];
82
86
 
83
- for (const opt of collectOptionDefs(root, path)) {
87
+ for (const opt of visibleOptions(collectOptionDefs(root, path))) {
84
88
  properties[opt.name] = optionProperty(opt);
85
89
  if (opt.required) {
86
90
  required.push(opt.name);
@@ -106,15 +110,21 @@ function buildInputSchema(root: CliProgram, path: string[], leaf: CliLeaf): Reco
106
110
  return schema;
107
111
  }
108
112
 
109
- /** Resolves MCP tool description with optional override and requiresEnv suffix. */
113
+ /** Resolves MCP tool description with optional override, requiresEnv suffix, and leaf notes. */
110
114
  function resolveToolDescription(root: CliProgram, path: string[], leaf: CliLeaf): string {
115
+ let desc: string;
111
116
  if (leaf.mcpTool?.description) {
112
- return leaf.mcpTool.description;
117
+ desc = leaf.mcpTool.description;
118
+ } else {
119
+ desc = mcpToolDescription(path, root.key, leaf.description);
120
+ const env = leaf.mcpTool?.requiresEnv;
121
+ if (env && env.length > 0) {
122
+ desc += ` [requires env: ${env.join(", ")}]`;
123
+ }
113
124
  }
114
- let desc = mcpToolDescription(path, root.key, leaf.description);
115
- const env = leaf.mcpTool?.requiresEnv;
116
- if (env && env.length > 0) {
117
- desc += ` [requires env: ${env.join(", ")}]`;
125
+ const notes = (leaf.notes ?? "").trim();
126
+ if (notes.length > 0) {
127
+ desc += `\n\n${cliResolveNotes(notes, root.key)}`;
118
128
  }
119
129
  return desc;
120
130
  }
@@ -155,18 +165,25 @@ export function collectMcpTools(root: CliProgram): McpToolDef[] {
155
165
  /** Walks the command tree and appends leaf tools. */
156
166
  function walk(cmd: CliNode, path: string[]): void {
157
167
  if (isCliLeaf(cmd)) {
158
- if (cmd.key === "completion" || cmd.key === "install" || cmd.key === "mcp" || cmd.key === "version") {
168
+ if (
169
+ cmd.key === "completion" ||
170
+ cmd.key === "install" ||
171
+ cmd.key === "mcp" ||
172
+ cmd.key === "version"
173
+ ) {
159
174
  return;
160
175
  }
161
- if (cmd.mcpTool?.enabled === false) {
176
+ if (cmd.hidden || cmd.mcpTool?.enabled === false) {
162
177
  return;
163
178
  }
179
+ const outputSchema = leafOutputSchema(cmd);
164
180
  out.push({
165
181
  name: mcpToolName(root, path),
166
182
  description: resolveToolDescription(root, path, cmd),
167
183
  path,
168
184
  leaf: cmd,
169
185
  inputSchema: buildInputSchema(root, path, cmd),
186
+ ...(outputSchema === undefined ? {} : { outputSchema }),
170
187
  });
171
188
  return;
172
189
  }
package/src/runtime.ts CHANGED
@@ -4,7 +4,7 @@ This module runs parsed commands, help, errors, completion, and leaf handlers.
4
4
 
5
5
  import { resolveCapabilities } from "./capabilities.ts";
6
6
  import { builtinInterceptRoot, dispatchBuiltin } from "./builtins/dispatch.ts";
7
- import { cliPresentationRoot } from "./builtins/presentation.ts";
7
+ import { cliParseRoot, cliPresentationRoot } from "./builtins/presentation.ts";
8
8
  import type { CliRouter } from "./types.ts";
9
9
  import { type CliNode, type CliProgram, isCliLeaf, isCliRouter } from "./types.ts";
10
10
  import { CliContext } from "./context.ts";
@@ -13,7 +13,7 @@ import { parse, postParseValidate, ParseKind } from "./parse.ts";
13
13
  import { cliValidateProgram } from "./validate.ts";
14
14
 
15
15
  function cliRootMergedWithBuiltins(program: CliProgram): CliRouter {
16
- return cliPresentationRoot(program);
16
+ return cliParseRoot(program);
17
17
  }
18
18
 
19
19
  export async function cliRun(program: CliProgram, argv: string[] = process.argv.slice(2)): Promise<never> {
@@ -68,7 +68,7 @@ export async function cliRun(program: CliProgram, argv: string[] = process.argv.
68
68
  pr = postParseValidate(parseRoot, pr);
69
69
 
70
70
  if (pr.kind === ParseKind.Help) {
71
- process.stdout.write(cliHelpRender(cliPresentationRoot(program), pr.helpPath, false));
71
+ process.stdout.write(cliHelpRender(cliParseRoot(program), pr.helpPath, false));
72
72
  process.exit(pr.helpExplicit ? 0 : 1);
73
73
  }
74
74
 
package/src/schema.ts CHANGED
@@ -2,13 +2,18 @@
2
2
  This module serializes the CLI schema tree to JSON for machine-readable introspection.
3
3
  */
4
4
 
5
- import { type CliNode, type CliProgram, isCliLeaf, isCliRouter } from "./types.ts";
5
+ import { type CliNode, type CliProgram, isCliLeaf, isCliRouter, leafOutputSchema } from "./types.ts";
6
6
  import { exportPresentationBuiltins, type CliSchemaExport } from "./builtins/export.ts";
7
7
  import { cliResolveNotes } from "./help.ts";
8
+ import { visibleOptions } from "./hidden.ts";
8
9
 
9
10
  const RESERVED = new Set(["completion", "install", "docs", "mcp", "version"]);
10
11
 
11
- function exportCommand(cmd: CliNode, root: CliProgram): CliSchemaExport {
12
+ function exportCommand(cmd: CliNode, root: CliProgram): CliSchemaExport | null {
13
+ if (cmd.hidden) {
14
+ return null;
15
+ }
16
+
12
17
  const out: CliSchemaExport = {
13
18
  key: cmd.key,
14
19
  description: cmd.description,
@@ -18,14 +23,19 @@ function exportCommand(cmd: CliNode, root: CliProgram): CliSchemaExport {
18
23
  out.notes = cmd.notes;
19
24
  }
20
25
 
21
- if ((cmd.options ?? []).length > 0) {
22
- out.options = cmd.options;
26
+ const options = visibleOptions(cmd.options);
27
+ if (options.length > 0) {
28
+ out.options = options;
23
29
  }
24
30
 
25
31
  if (isCliLeaf(cmd)) {
26
32
  if ((cmd.positionals ?? []).length > 0) {
27
33
  out.positionals = cmd.positionals;
28
34
  }
35
+ const outputSchema = leafOutputSchema(cmd);
36
+ if (outputSchema !== undefined) {
37
+ out.outputSchema = outputSchema;
38
+ }
29
39
  out.commands = exportPresentationBuiltins(root);
30
40
  return out;
31
41
  }
@@ -39,7 +49,9 @@ function exportCommand(cmd: CliNode, root: CliProgram): CliSchemaExport {
39
49
 
40
50
  const children = isCliRouter(cmd) ? cmd.commands.filter((ch) => !RESERVED.has(ch.key)) : [];
41
51
  if (children.length > 0) {
42
- out.commands = children.map((ch) => exportCommand(ch, root));
52
+ out.commands = children
53
+ .map((ch) => exportCommand(ch, root))
54
+ .filter((ch): ch is CliSchemaExport => ch !== null);
43
55
  }
44
56
 
45
57
  return out;
@@ -59,7 +71,15 @@ function resolveSchemaNotes(node: CliSchemaExport, appKey: string): CliSchemaExp
59
71
 
60
72
  /** Returns the JSON-safe command tree (handlers omitted). */
61
73
  export function cliSchemaExport(root: CliProgram): CliSchemaExport {
62
- return resolveSchemaNotes(exportCommand(root, root), root.key);
74
+ const exported = exportCommand(root, root);
75
+ if (!exported) {
76
+ return {
77
+ key: root.key,
78
+ description: root.description,
79
+ commands: exportPresentationBuiltins(root),
80
+ };
81
+ }
82
+ return resolveSchemaNotes(exported, root.key);
63
83
  }
64
84
 
65
85
  export function cliSchemaJson(root: CliProgram): string {
package/src/types.ts CHANGED
@@ -50,6 +50,8 @@ export enum CliFallbackMode {
50
50
  export interface CliOption {
51
51
  /** Option name (e.g., "name", "verbose"). */
52
52
  name: string;
53
+ /** When `true`, omit from help, schema, completions, and MCP tool inputSchema (still parseable). */
54
+ hidden?: boolean;
53
55
  /** Description shown in help. */
54
56
  description: string;
55
57
  /** Option kind: presence flag, string value, or number value. */
@@ -87,6 +89,19 @@ export interface CliPositional {
87
89
  argMax?: number;
88
90
  }
89
91
 
92
+ /** Optional metadata for `mcp bundle` MCP Bundle output (program root `mcpServer.bundle` only). */
93
+ export interface CliMcpBundleConfig {
94
+ author?: {
95
+ name: string;
96
+ email?: string;
97
+ url?: string;
98
+ };
99
+ /** Repo-relative path to a PNG icon copied into the bundle. */
100
+ icon?: string;
101
+ /** Manifest `long_description` (defaults to program description). */
102
+ longDescription?: string;
103
+ }
104
+
90
105
  /**
91
106
  * Enables `myapp mcp` and MCP stdio server metadata (program root only).
92
107
  * Must include `enabled: true`; omit `mcpServer` entirely to disable MCP.
@@ -113,6 +128,8 @@ export interface CliMcpServerConfig {
113
128
  * URIs must be unique and must not equal schemaResourceUri.
114
129
  */
115
130
  resources?: CliMcpResource[];
131
+ /** Optional MCP Bundle (`.mcpb`) metadata for `mcp bundle`. */
132
+ bundle?: CliMcpBundleConfig;
116
133
  }
117
134
 
118
135
  /**
@@ -148,6 +165,10 @@ export interface CliMcpToolConfig {
148
165
  * Empty string counts as absent.
149
166
  */
150
167
  requiresEnv?: string[];
168
+ /**
169
+ * @deprecated Set `outputSchema` on the leaf command instead.
170
+ */
171
+ outputSchema?: Record<string, unknown>;
151
172
  }
152
173
 
153
174
  /**
@@ -211,6 +232,8 @@ export interface CliDocsConfig {
211
232
  export interface CliNodeBase {
212
233
  /** Program or command key (e.g., "myapp", "stat", "owner"). */
213
234
  key: string;
235
+ /** When `true`, omit from help listings, schema, completions, and MCP tools (still invocable). */
236
+ hidden?: boolean;
214
237
  /** Short description shown in help. */
215
238
  description: string;
216
239
  /** Additional notes shown in help (`{argsbarg:program}` → program key). */
@@ -227,6 +250,11 @@ export type CliLeaf = CliNodeBase & {
227
250
  handler: CliHandler;
228
251
  /** Positional argument definitions. */
229
252
  positionals?: CliPositional[];
253
+ /**
254
+ * JSON Schema for structured stdout (e.g. with `--json` or MCP when the handler emits JSON).
255
+ * Exported in `docs schema`, `docs api`, and MCP `tools/list`; not validated at runtime yet.
256
+ */
257
+ outputSchema?: Record<string, unknown>;
230
258
  /** Per-tool MCP exposure and metadata. */
231
259
  mcpTool?: CliMcpToolConfig;
232
260
  };
@@ -273,6 +301,11 @@ export function isCliRouter(node: CliNode): node is CliRouter {
273
301
  return "commands" in node && Array.isArray(node.commands);
274
302
  }
275
303
 
304
+ /** Resolves structured stdout schema from the leaf (prefers leaf field over legacy `mcpTool.outputSchema`). */
305
+ export function leafOutputSchema(leaf: CliLeaf): Record<string, unknown> | undefined {
306
+ return leaf.outputSchema ?? leaf.mcpTool?.outputSchema;
307
+ }
308
+
276
309
  /**
277
310
  * Handler closure type for leaf commands.
278
311
  * Supports both sync and async handlers.
package/src/validate.ts CHANGED
@@ -112,6 +112,22 @@ function walkNode(node: CliNode, program: CliProgram, isRoot: boolean): void {
112
112
  if (isRoot && node.mcpTool !== undefined) {
113
113
  throw new CliSchemaValidationError("mcpTool is only supported on leaf commands");
114
114
  }
115
+ const outputSchema = node.outputSchema;
116
+ const legacyOutputSchema = node.mcpTool?.outputSchema;
117
+ if (outputSchema !== undefined && legacyOutputSchema !== undefined) {
118
+ throw new CliSchemaValidationError(
119
+ "Set outputSchema on the leaf only, not under mcpTool",
120
+ );
121
+ }
122
+ const resolved = outputSchema ?? legacyOutputSchema;
123
+ if (
124
+ resolved !== undefined &&
125
+ (typeof resolved !== "object" || resolved === null || Array.isArray(resolved))
126
+ ) {
127
+ throw new CliSchemaValidationError(
128
+ "outputSchema must be a JSON Schema object (not null or an array)",
129
+ );
130
+ }
115
131
  } else {
116
132
  const rogue = node as unknown as CliLeaf;
117
133
  if (rogue.mcpTool !== undefined) {