argsbarg 3.3.14 → 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.
- package/.private/scratch.md +3 -1
- package/CHANGELOG.md +12 -1
- package/docs/cli-program.md +19 -0
- package/docs/mcp.md +23 -3
- package/index.d.ts +47 -0
- package/package.json +1 -1
- package/src/builtins/dispatch.ts +16 -5
- package/src/builtins/export.ts +31 -17
- package/src/builtins/index.ts +1 -1
- package/src/builtins/mcp.ts +21 -5
- package/src/builtins/presentation.ts +46 -7
- package/src/docs/api-guide.test.ts +30 -0
- package/src/docs/api-guide.ts +18 -0
- package/src/help.ts +5 -4
- package/src/hidden-mcpb.test.ts +154 -0
- package/src/hidden.ts +32 -0
- package/src/index.test.ts +175 -1
- package/src/index.ts +3 -0
- package/src/invoke.ts +2 -2
- package/src/mcp/bundle.ts +251 -0
- package/src/mcp/server.ts +1 -0
- package/src/mcp/tools.ts +27 -10
- package/src/runtime.ts +3 -3
- package/src/schema.ts +26 -6
- package/src/types.ts +33 -0
- package/src/validate.ts +16 -0
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
|
|
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
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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 (
|
|
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
|
|
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(
|
|
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
|
-
|
|
22
|
-
|
|
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
|
|
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
|
-
|
|
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) {
|