argsbarg 1.3.0 → 1.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/.cursor/plans/mcp_v1.1_polish_e9656029.plan.md +260 -0
- package/.private/scratch.md +1 -1
- package/CHANGELOG.md +24 -1
- package/README.md +17 -2
- package/docs/mcp.md +211 -0
- package/examples/nested.ts +1 -0
- package/index.d.ts +22 -0
- package/justfile +4 -4
- package/package.json +1 -1
- package/src/completion.ts +61 -2
- package/src/help.ts +8 -4
- package/src/index.test.ts +422 -0
- package/src/index.ts +1 -1
- package/src/invoke.ts +192 -0
- package/src/mcp/result.ts +57 -0
- package/src/mcp/server.ts +226 -0
- package/src/mcp/tools.ts +209 -0
- package/src/mcp.ts +24 -0
- package/src/parse.ts +2 -2
- package/src/runtime.ts +29 -7
- package/src/schema.ts +16 -0
- package/src/types.ts +24 -0
- package/src/validate.ts +16 -1
package/src/parse.ts
CHANGED
|
@@ -217,7 +217,7 @@ function consumeOptions(
|
|
|
217
217
|
// ── Positional Collection ─────────────────────────────────────────────────────
|
|
218
218
|
|
|
219
219
|
/** Merges option defs from the program root along the routed command path. */
|
|
220
|
-
function collectOptionDefs(root: CliCommand, path: string[]): CliOption[] {
|
|
220
|
+
export function collectOptionDefs(root: CliCommand, path: string[]): CliOption[] {
|
|
221
221
|
let defs = [...(root.options ?? [])];
|
|
222
222
|
let cmds = root.commands ?? [];
|
|
223
223
|
|
|
@@ -458,7 +458,7 @@ export function parse(root: CliCommand, argv: string[]): ParseResult {
|
|
|
458
458
|
// Walk the command tree
|
|
459
459
|
while (true) {
|
|
460
460
|
if (!forcePositionals) {
|
|
461
|
-
const orep = consumeOptions(
|
|
461
|
+
const orep = consumeOptions(collectOptionDefs(root, path), false, argv, i, opts);
|
|
462
462
|
if (orep.report.err) {
|
|
463
463
|
return {
|
|
464
464
|
kind: ParseKind.Error,
|
package/src/runtime.ts
CHANGED
|
@@ -7,9 +7,10 @@ It keeps execution flow out of the public barrel so the exported API stays small
|
|
|
7
7
|
the runtime responsibilities remain easy to reason about.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { cliBuiltinCompletionGroup, completionBashScript, completionZshScript } from "./completion.ts";
|
|
10
|
+
import { cliBuiltinCompletionGroup, cliBuiltinMcpCommand, cliPresentationRoot, completionBashScript, completionZshScript } from "./completion.ts";
|
|
11
11
|
import { CliContext } from "./context.ts";
|
|
12
12
|
import { cliHelpRender } from "./help.ts";
|
|
13
|
+
import { cliMcpServeStdio } from "./mcp.ts";
|
|
13
14
|
import { parse, postParseValidate, ParseKind } from "./parse.ts";
|
|
14
15
|
import { cliSchemaJson } from "./schema.ts";
|
|
15
16
|
import { CliCommand } from "./types.ts";
|
|
@@ -22,9 +23,7 @@ function cliRootMergedWithBuiltins(root: CliCommand): CliCommand {
|
|
|
22
23
|
if (root.handler) {
|
|
23
24
|
return root;
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
-
merged.commands = [...(root.commands ?? []), cliBuiltinCompletionGroup(root.key)];
|
|
27
|
-
return merged as CliCommand;
|
|
26
|
+
return cliPresentationRoot(root);
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
/**
|
|
@@ -48,6 +47,11 @@ export async function cliRun(root: CliCommand, argv: string[] = process.argv.sli
|
|
|
48
47
|
let parseRoot = root;
|
|
49
48
|
let isLeafCompletionIntercept = false;
|
|
50
49
|
|
|
50
|
+
if (root.handler && argv.length >= 1 && argv[0] === "mcp" && !root.mcpServer) {
|
|
51
|
+
process.stderr.write("Unknown command: mcp\n");
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
51
55
|
// Intercept completion for Leaf roots (since they can't natively have a completion subcommand)
|
|
52
56
|
// but wrap them in a dummy router so that the parser handles `-h` and errors correctly.
|
|
53
57
|
if (root.handler && argv.length >= 1 && argv[0] === "completion") {
|
|
@@ -57,6 +61,12 @@ export async function cliRun(root: CliCommand, argv: string[] = process.argv.sli
|
|
|
57
61
|
description: root.description,
|
|
58
62
|
commands: [cliBuiltinCompletionGroup(root.key)],
|
|
59
63
|
} as any;
|
|
64
|
+
} else if (root.handler && argv.length >= 1 && argv[0] === "mcp" && root.mcpServer) {
|
|
65
|
+
parseRoot = {
|
|
66
|
+
key: root.key,
|
|
67
|
+
description: root.description,
|
|
68
|
+
commands: [cliBuiltinMcpCommand()],
|
|
69
|
+
} as CliCommand;
|
|
60
70
|
} else {
|
|
61
71
|
parseRoot = cliRootMergedWithBuiltins(root);
|
|
62
72
|
}
|
|
@@ -65,7 +75,7 @@ export async function cliRun(root: CliCommand, argv: string[] = process.argv.sli
|
|
|
65
75
|
pr = postParseValidate(parseRoot, pr);
|
|
66
76
|
|
|
67
77
|
if (pr.kind === ParseKind.Help) {
|
|
68
|
-
process.stdout.write(cliHelpRender(
|
|
78
|
+
process.stdout.write(cliHelpRender(cliPresentationRoot(root), pr.helpPath, false));
|
|
69
79
|
process.exit(pr.helpExplicit ? 0 : 1);
|
|
70
80
|
}
|
|
71
81
|
|
|
@@ -78,7 +88,7 @@ export async function cliRun(root: CliCommand, argv: string[] = process.argv.sli
|
|
|
78
88
|
const color = process.stderr.isTTY;
|
|
79
89
|
const msg = color ? `\u001B[31m${pr.errorMsg}\u001B[0m` : pr.errorMsg;
|
|
80
90
|
process.stderr.write(msg + "\n");
|
|
81
|
-
process.stderr.write(cliHelpRender(
|
|
91
|
+
process.stderr.write(cliHelpRender(cliPresentationRoot(root), pr.errorHelpPath, true));
|
|
82
92
|
process.exit(1);
|
|
83
93
|
}
|
|
84
94
|
|
|
@@ -99,6 +109,18 @@ export async function cliRun(root: CliCommand, argv: string[] = process.argv.sli
|
|
|
99
109
|
}
|
|
100
110
|
}
|
|
101
111
|
|
|
112
|
+
if (pr.path[0] === "mcp") {
|
|
113
|
+
if (!root.mcpServer) {
|
|
114
|
+
process.stderr.write("Internal error: mcp not enabled.\n");
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
if (pr.path.length !== 1) {
|
|
118
|
+
process.stderr.write("Unknown subcommand: mcp " + pr.path.slice(1).join(" ") + "\n");
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
await cliMcpServeStdio(root);
|
|
122
|
+
}
|
|
123
|
+
|
|
102
124
|
let current = parseRoot;
|
|
103
125
|
for (const seg of pr.path) {
|
|
104
126
|
const ch = (current.commands ?? []).find((candidate: CliCommand) => candidate.key === seg);
|
|
@@ -133,6 +155,6 @@ export function cliErrWithHelp(ctx: CliContext, msg: string): never {
|
|
|
133
155
|
const color = process.stderr.isTTY;
|
|
134
156
|
const line = color ? `\u001B[31m${msg}\u001B[0m` : msg;
|
|
135
157
|
process.stderr.write(line + "\n");
|
|
136
|
-
process.stderr.write(cliHelpRender(ctx.schema, ctx.commandPath, true));
|
|
158
|
+
process.stderr.write(cliHelpRender(cliPresentationRoot(ctx.schema), ctx.commandPath, true));
|
|
137
159
|
process.exit(1);
|
|
138
160
|
}
|
package/src/schema.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
CliOption,
|
|
14
14
|
CliPositional,
|
|
15
15
|
} from "./types.ts";
|
|
16
|
+
import { cliBuiltinCompletionGroup } from "./completion.ts";
|
|
16
17
|
|
|
17
18
|
/** JSON-safe command node (no handlers). */
|
|
18
19
|
export interface CliSchemaExport {
|
|
@@ -34,6 +35,20 @@ export interface CliSchemaExport {
|
|
|
34
35
|
positionals?: CliPositional[];
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
/** JSON-safe export of the reserved `completion` subtree (no handler recursion). */
|
|
39
|
+
function exportBuiltinCompletionGroup(appName: string): CliSchemaExport {
|
|
40
|
+
const group = cliBuiltinCompletionGroup(appName);
|
|
41
|
+
return {
|
|
42
|
+
key: group.key,
|
|
43
|
+
description: group.description,
|
|
44
|
+
commands: (group.commands ?? []).map((ch) => ({
|
|
45
|
+
key: ch.key,
|
|
46
|
+
description: ch.description,
|
|
47
|
+
...((ch.notes ?? "").length > 0 ? { notes: ch.notes } : {}),
|
|
48
|
+
})),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
37
52
|
/** Converts one `CliCommand` node into a JSON-safe export (handlers omitted). */
|
|
38
53
|
function exportCommand(cmd: CliCommand): CliSchemaExport {
|
|
39
54
|
const out: CliSchemaExport = {
|
|
@@ -53,6 +68,7 @@ function exportCommand(cmd: CliCommand): CliSchemaExport {
|
|
|
53
68
|
if ((cmd.positionals ?? []).length > 0) {
|
|
54
69
|
out.positionals = cmd.positionals;
|
|
55
70
|
}
|
|
71
|
+
out.commands = [exportBuiltinCompletionGroup(cmd.key)];
|
|
56
72
|
return out;
|
|
57
73
|
}
|
|
58
74
|
|
package/src/types.ts
CHANGED
|
@@ -78,6 +78,26 @@ export interface CliPositional {
|
|
|
78
78
|
argMax?: number;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Root-only. Enables `myapp mcp` and MCP stdio server metadata.
|
|
83
|
+
*/
|
|
84
|
+
export interface CliMcpServerConfig {
|
|
85
|
+
/** `initialize` serverInfo.name (default: root `key`). */
|
|
86
|
+
name?: string;
|
|
87
|
+
/** `initialize` serverInfo.version (default: see resolveMcpVersion). */
|
|
88
|
+
version?: string;
|
|
89
|
+
/** Resource URI for schema export (default: `"argsbarg://schema"`). */
|
|
90
|
+
schemaResourceUri?: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Leaf-only. Controls how this command appears as an MCP tool.
|
|
95
|
+
*/
|
|
96
|
+
export interface CliMcpToolConfig {
|
|
97
|
+
/** When `false`, omit from `tools/list` (default: exposed). */
|
|
98
|
+
enabled?: boolean;
|
|
99
|
+
}
|
|
100
|
+
|
|
81
101
|
/**
|
|
82
102
|
* Base properties shared by all command nodes.
|
|
83
103
|
*/
|
|
@@ -90,6 +110,10 @@ export interface CliCommandBase {
|
|
|
90
110
|
notes?: string;
|
|
91
111
|
/** Global or command-level flags/options. */
|
|
92
112
|
options?: CliOption[];
|
|
113
|
+
/** Root-only. When set, enables the `mcp` built-in subcommand. */
|
|
114
|
+
mcpServer?: CliMcpServerConfig;
|
|
115
|
+
/** Leaf-only. Per-tool MCP exposure and metadata. */
|
|
116
|
+
mcpTool?: CliMcpToolConfig;
|
|
93
117
|
}
|
|
94
118
|
|
|
95
119
|
/**
|
package/src/validate.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
CliSchemaValidationError,
|
|
14
14
|
} from "./types.ts";
|
|
15
15
|
|
|
16
|
-
const reservedCommandNames = ["completion"];
|
|
16
|
+
const reservedCommandNames = ["completion", "mcp"];
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Validates the static CliCommand tree against ArgBarg rules.
|
|
@@ -50,6 +50,21 @@ function walkCommand(cmd: CliCommand, isRoot: boolean = false): void {
|
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
if (!isRoot && cmd.mcpServer !== undefined) {
|
|
54
|
+
throw new CliSchemaValidationError(
|
|
55
|
+
"mcpServer is only supported on the program root (not on " + cmd.key + ")",
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const isLeaf = "handler" in cmd && !!cmd.handler;
|
|
60
|
+
if (!isLeaf && cmd.mcpTool !== undefined) {
|
|
61
|
+
throw new CliSchemaValidationError(
|
|
62
|
+
"mcpTool is only supported on leaf commands (not on " + cmd.key + ")",
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
if (isRoot && cmd.mcpTool !== undefined) {
|
|
66
|
+
throw new CliSchemaValidationError("mcpTool is only supported on leaf commands");
|
|
67
|
+
}
|
|
53
68
|
|
|
54
69
|
// Check for duplicate child names
|
|
55
70
|
const seenNames = new Set<string>();
|