@alcyone-labs/arg-parser 2.13.0 → 2.13.2
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/README.md +45 -2718
- package/dist/core/ArgParserBase.d.ts.map +1 -1
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.min.mjs +21 -21
- package/dist/index.min.mjs.map +1 -1
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,109 +7,11 @@ A modern, type-safe command line argument parser with built-in MCP (Model Contex
|
|
|
7
7
|
- [Features Overview](#features-overview)
|
|
8
8
|
- [Installation](#installation)
|
|
9
9
|
- [Quick Start: The Unified `addTool` API](#quick-start-the-unified-addtool-api)
|
|
10
|
-
- [
|
|
11
|
-
- [MCP Tool Name Constraints](#mcp-tool-name-constraints)
|
|
10
|
+
- [Documentation](#documentation)
|
|
12
11
|
- [How to Run It](#how-to-run-it)
|
|
13
|
-
- [Setting Up System-Wide CLI Access](#setting-up-system-wide-cli-access)
|
|
14
12
|
- [OpenTUI: Reactive Rich Terminal Interfaces](#opentui-reactive-rich-terminal-interfaces)
|
|
15
|
-
- [Parsing Command-Line Arguments](#parsing-command-line-arguments)
|
|
16
|
-
- [Automatic Argument Detection](#automatic-argument-detection)
|
|
17
|
-
- [Cannonical Usage Pattern](#cannonical-usage-pattern)
|
|
18
|
-
- [Top-level await](#top-level-await)
|
|
19
|
-
- [Promise-based parsing](#promise-based-parsing)
|
|
20
|
-
- [Migrating from v1.x to the v2.0 `addTool` API](#migrating-from-v1x-to-the-v20-addtool-api)
|
|
21
|
-
- [Before v2.0: Separate Definitions](#before-v20-separate-definitions)
|
|
22
|
-
- [After v2.0: The Unified `addTool()` Method](#after-v20-the-unified-addtool-method)
|
|
23
|
-
- [Core Concepts](#core-concepts)
|
|
24
|
-
- [Defining Flags](#defining-flags)
|
|
25
|
-
- [Type Handling and Validation](#type-handling-and-validation)
|
|
26
|
-
- [Supported Type Formats](#supported-type-formats)
|
|
27
|
-
- [Runtime Type Validation](#runtime-type-validation)
|
|
28
|
-
- [Automatic Type Processing](#automatic-type-processing)
|
|
29
|
-
- [Async Custom Parser Support](#async-custom-parser-support)
|
|
30
|
-
- [Zod Schema Flags (Structured JSON Validation)](#zod-schema-flags-structured-json-validation)
|
|
31
|
-
- [Type Conversion Examples](#type-conversion-examples)
|
|
32
|
-
- [DXT Package User Configuration & Path Handling](#dxt-package-user-configuration--path-handling)
|
|
33
|
-
- [Hierarchical CLIs (Sub-Commands)](#hierarchical-clis-sub-commands)
|
|
34
|
-
- [MCP Exposure Control](#mcp-exposure-control)
|
|
35
|
-
- [Flag Inheritance (`inheritParentFlags`)](#flag-inheritance-inheritparentflags)
|
|
36
|
-
- [Dynamic Flags (`dynamicRegister`)](#dynamic-flags-dynamicregister)
|
|
37
|
-
- [Positional Arguments](#positional-arguments)
|
|
38
|
-
- [Automatic Help Display](#automatic-help-display)
|
|
39
|
-
|
|
40
|
-
- [MCP & Claude Desktop Integration](#mcp--claude-desktop-integration)
|
|
41
|
-
- [Output Schema Support](#output-schema-support)
|
|
42
|
-
- [Basic Usage](#basic-usage)
|
|
43
|
-
- [Predefined Schema Patterns](#predefined-schema-patterns)
|
|
44
|
-
- [Custom Zod Schemas](#custom-zod-schemas)
|
|
45
|
-
- [MCP Version Compatibility](#mcp-version-compatibility)
|
|
46
|
-
- [Automatic Error Handling](#automatic-error-handling)
|
|
47
|
-
- [Writing Effective MCP Tool Descriptions](#writing-effective-mcp-tool-descriptions)
|
|
48
|
-
- [Best Practices for Tool Descriptions](#best-practices-for-tool-descriptions)
|
|
49
|
-
- [Complete Example: Well-Documented Tool](#complete-example-well-documented-tool)
|
|
50
|
-
- [Parameter Description Guidelines](#parameter-description-guidelines)
|
|
51
|
-
- [Common Pitfalls to Avoid](#common-pitfalls-to-avoid)
|
|
52
|
-
- [Automatic MCP Server Mode (`--s-mcp-serve`)](#automatic-mcp-server-mode---s-mcp-serve)
|
|
53
|
-
- [MCP Transports](#mcp-transports)
|
|
54
|
-
- [Adding custom HTTP routes (e.g., /health)](#adding-custom-http-routes-eg-health)
|
|
55
|
-
- [CORS and Authentication for streamable-http](#cors-and-authentication-for-streamable-http)
|
|
56
|
-
- [Multiple transports and improved logging](#multiple-transports-and-improved-logging)
|
|
57
|
-
- [MCP Logging Configuration](#mcp-logging-configuration)
|
|
58
|
-
- [Enhanced Logging (Recommended)](#enhanced-logging-recommended)
|
|
59
|
-
- [Simple Logging Configuration](#simple-logging-configuration)
|
|
60
|
-
- [Configuration Priority](#configuration-priority)
|
|
61
|
-
- [Configuration Merging](#configuration-merging)
|
|
62
|
-
- [Path Resolution Options](#path-resolution-options)
|
|
63
|
-
- [MCP Lifecycle Events](#mcp-lifecycle-events)
|
|
64
|
-
- [MCP Resources - Real-Time Data Feeds](#mcp-resources---real-time-data-feeds)
|
|
65
|
-
- [Basic Resource Setup](#basic-resource-setup)
|
|
66
|
-
- [URI Templates with Dynamic Parameters](#uri-templates-with-dynamic-parameters)
|
|
67
|
-
- [MCP Subscription Lifecycle](#mcp-subscription-lifecycle)
|
|
68
|
-
- [Usage Examples](#usage-examples)
|
|
69
|
-
- [Design Patterns](#design-patterns)
|
|
70
|
-
- [Automatic Console Safety](#automatic-console-safety)
|
|
71
|
-
- [Generating DXT Packages (`--s-build-dxt`)](#generating-dxt-packages---s-build-dxt)
|
|
72
|
-
- [Logo Configuration](#logo-configuration)
|
|
73
|
-
- [Supported Logo Sources](#supported-logo-sources)
|
|
74
|
-
- [Including Additional Files in DXT Packages](#including-additional-files-in-dxt-packages)
|
|
75
|
-
- [Include Options](#include-options)
|
|
76
|
-
- [How DXT Generation Works](#how-dxt-generation-works)
|
|
77
|
-
- [DXT Bundling Strategies](#dxt-bundling-strategies)
|
|
78
|
-
- [Standard Approach (Recommended for Most Projects)](#standard-approach-recommended-for-most-projects)
|
|
79
|
-
- [Native Dependencies Approach](#native-dependencies-approach)
|
|
80
|
-
- [Typical Errors](#typical-errors)
|
|
81
13
|
- [System Flags & Configuration](#system-flags--configuration)
|
|
82
|
-
- [
|
|
83
|
-
- [v2.13.0](#v2130)
|
|
84
|
-
- [v2.12.3](#v2123)
|
|
85
|
-
- [v2.12.2](#v2122)
|
|
86
|
-
- [v2.12.0](#v2120)
|
|
87
|
-
- [v2.11.0](#v2110)
|
|
88
|
-
- [v2.10.3](#v2103)
|
|
89
|
-
- [v2.10.2](#v2102)
|
|
90
|
-
- [v2.10.1](#v2101)
|
|
91
|
-
- [v2.10.0](#v2100)
|
|
92
|
-
- [v2.8.2](#v282)
|
|
93
|
-
- [v2.8.1](#v281)
|
|
94
|
-
- [v2.7.2](#v272)
|
|
95
|
-
- [v2.7.0](#v270)
|
|
96
|
-
- [v2.6.0](#v260)
|
|
97
|
-
- [v2.5.0](#v250)
|
|
98
|
-
- [v2.4.2](#v242)
|
|
99
|
-
- [v2.4.1](#v241)
|
|
100
|
-
- [v2.4.0](#v240)
|
|
101
|
-
- [v2.3.0](#v230)
|
|
102
|
-
- [v2.2.1](#v221)
|
|
103
|
-
- [v2.2.0](#v220)
|
|
104
|
-
- [v2.1.1](#v211)
|
|
105
|
-
- [v2.1.0](#v210)
|
|
106
|
-
- [v2.0.0](#v200)
|
|
107
|
-
- [v1.3.0](#v130)
|
|
108
|
-
- [v1.2.0](#v120)
|
|
109
|
-
- [v1.1.0](#v110)
|
|
110
|
-
|
|
111
|
-
- [Backlog](#backlog)
|
|
112
|
-
- [(known) Bugs / DX improvement points](#known-bugs--dx-improvement-points)
|
|
14
|
+
- [Links](#links)
|
|
113
15
|
|
|
114
16
|
## Features Overview
|
|
115
17
|
|
|
@@ -117,114 +19,11 @@ A modern, type-safe command line argument parser with built-in MCP (Model Contex
|
|
|
117
19
|
- **Type-safe flag definitions** with full TypeScript support and autocompletion.
|
|
118
20
|
- **Automatic MCP Integration**: Transform any CLI into a compliant MCP server with a single command (`--s-mcp-serve`).
|
|
119
21
|
- **MCP Resources with Real-Time Feeds** ⭐: Create subscription-based data feeds with URI templates for live notifications to AI assistants.
|
|
120
|
-
- **Console Safe**: `console.log` and other methods
|
|
121
|
-
|
|
122
|
-
- **
|
|
123
|
-
- **
|
|
124
|
-
- **
|
|
125
|
-
- **OpenTUI Framework** ⭐: A standardized, event-driven TUI engine with mouse support, multi-layer navigation, and reactive themes (Dark, Ocean, Monokai).
|
|
126
|
-
|
|
127
|
-
---
|
|
128
|
-
|
|
129
|
-
## OpenTUI: Reactive Rich Terminal Interfaces
|
|
130
|
-
|
|
131
|
-
ArgParser includes **OpenTUI v2**, a reactive TUI framework built on SolidJS for building rich terminal applications with minimal boilerplate.
|
|
132
|
-
|
|
133
|
-
> 📖 **Full Documentation**: [docs/TUI.md](./docs/TUI.md)
|
|
134
|
-
|
|
135
|
-
- **Standardized Navigation**: `Enter` / `Right` to dive into details, `Esc` / `Left` to go back.
|
|
136
|
-
- **Reactive Theming**: Cycle through built-in themes (`Default`, `Ocean`, `Monokai`) or create your own.
|
|
137
|
-
- **Mouse & Scroll Performance**: Built-in SGR mouse reporting support with smooth scrolling and high-performance rendering.
|
|
138
|
-
- **New Components**: `Label` (Text), `Button` (Interactive), `Card` (Container), `Toast` (Notification).
|
|
139
|
-
- **Component Based**: Reusable `List`, `ScrollArea`, `Input`, and `SplitLayout` components.
|
|
140
|
-
- **TuiProvider**: Unified provider handling mouse, resize, cleanup, themes, and shortcuts
|
|
141
|
-
- **Virtual Scrolling**: Efficient list rendering with `VirtualList`
|
|
142
|
-
- **Theme System**: 6 built-in themes + `Theme.from().extend()` for custom themes
|
|
143
|
-
- **Slot-based Layouts**: `MasterDetail` with customizable panels
|
|
144
|
-
- **Keyboard + Mouse**: Built-in navigation with useKeyboard and mouse wheel
|
|
145
|
-
|
|
146
|
-
### Quick Start
|
|
147
|
-
|
|
148
|
-
```tsx
|
|
149
|
-
import { createSignal } from "solid-js";
|
|
150
|
-
import {
|
|
151
|
-
cleanupTerminal,
|
|
152
|
-
createVirtualListController,
|
|
153
|
-
MasterDetail,
|
|
154
|
-
TuiProvider,
|
|
155
|
-
useTheme,
|
|
156
|
-
useTui,
|
|
157
|
-
VirtualList,
|
|
158
|
-
} from "@alcyone-labs/arg-parser/tui";
|
|
159
|
-
import { render, useKeyboard } from "@opentui/solid";
|
|
160
|
-
|
|
161
|
-
const DATA = [{ id: "1", name: "Item 1" } /* ... */];
|
|
162
|
-
|
|
163
|
-
function App() {
|
|
164
|
-
const { viewportHeight, exit } = useTui();
|
|
165
|
-
const { current: theme, cycle } = useTheme();
|
|
166
|
-
const [idx, setIdx] = createSignal(0);
|
|
167
|
-
|
|
168
|
-
const list = createVirtualListController(
|
|
169
|
-
() => DATA,
|
|
170
|
-
idx,
|
|
171
|
-
setIdx,
|
|
172
|
-
viewportHeight,
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
useKeyboard((key) => {
|
|
176
|
-
if (key.name === "q") exit(0);
|
|
177
|
-
if (key.name === "t") cycle();
|
|
178
|
-
if (key.name === "down") list.selectNext();
|
|
179
|
-
if (key.name === "up") list.selectPrevious();
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
return (
|
|
183
|
-
<MasterDetail
|
|
184
|
-
header="My App"
|
|
185
|
-
breadcrumb={["Items", DATA[idx()]!.name]}
|
|
186
|
-
footer={`↑↓: Navigate | t: Theme (${theme().name}) | q: Quit`}
|
|
187
|
-
master={
|
|
188
|
-
<VirtualList
|
|
189
|
-
items={DATA}
|
|
190
|
-
selectedIndex={idx()}
|
|
191
|
-
viewportHeight={viewportHeight()}
|
|
192
|
-
getLabel={(item) => item.name}
|
|
193
|
-
/>
|
|
194
|
-
}
|
|
195
|
-
detail={<text>Selected: {DATA[idx()]!.name}</text>}
|
|
196
|
-
/>
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
render(
|
|
201
|
-
() => (
|
|
202
|
-
<TuiProvider theme="dark">
|
|
203
|
-
<App />
|
|
204
|
-
</TuiProvider>
|
|
205
|
-
),
|
|
206
|
-
{ onDestroy: cleanupTerminal },
|
|
207
|
-
);
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### Theme Builder
|
|
211
|
-
|
|
212
|
-
```ts
|
|
213
|
-
import { Theme, THEMES } from "@alcyone-labs/arg-parser/tui";
|
|
214
|
-
|
|
215
|
-
// Built-in: dark, light, monokai, dracula, nord, solarized
|
|
216
|
-
const custom = Theme.from(THEMES.dark).extend({
|
|
217
|
-
name: "my-theme",
|
|
218
|
-
colors: { background: "#1e1e1e", accent: "#ff6b6b" },
|
|
219
|
-
});
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### Run Examples
|
|
223
|
-
|
|
224
|
-
```bash
|
|
225
|
-
bun examples/framework-demo.tsx # Simplified demo
|
|
226
|
-
bun examples/aquaria-trace-viewer.tsx # Full-featured demo
|
|
227
|
-
```
|
|
22
|
+
- **Console Safe**: `console.log` and other methods are automatically handled in MCP mode to prevent protocol contamination.
|
|
23
|
+
- **DXT Package Generation**: Generate complete, ready-to-install Claude Desktop Extension (`.dxt`) packages.
|
|
24
|
+
- **Hierarchical Sub-commands**: Create complex, nested sub-command structures with flag inheritance.
|
|
25
|
+
- **Configuration Management**: Easily load (`--s-with-env`) and save (`--s-save-to-env`) configurations.
|
|
26
|
+
- **OpenTUI Framework** ⭐: A reactive TUI engine built on SolidJS with mouse support and themes.
|
|
228
27
|
|
|
229
28
|
## Installation
|
|
230
29
|
|
|
@@ -233,391 +32,14 @@ bun examples/aquaria-trace-viewer.tsx # Full-featured demo
|
|
|
233
32
|
pnpm add @alcyone-labs/arg-parser
|
|
234
33
|
```
|
|
235
34
|
|
|
236
|
-
---
|
|
237
|
-
|
|
238
35
|
## Quick Start: The Unified `addTool` API
|
|
239
36
|
|
|
240
|
-
The modern way to build with ArgParser is using the `.addTool()` method.
|
|
37
|
+
The modern way to build with ArgParser is using the `.addTool()` method.
|
|
241
38
|
|
|
242
39
|
```typescript
|
|
243
40
|
import { z } from "zod";
|
|
244
41
|
import { ArgParser } from "@alcyone-labs/arg-parser";
|
|
245
42
|
|
|
246
|
-
// Use ArgParser.withMcp to enable MCP and DXT features
|
|
247
|
-
const cli = ArgParser.withMcp({
|
|
248
|
-
appName: "My Awesome CLI",
|
|
249
|
-
appCommandName: "mycli",
|
|
250
|
-
description: "A tool that works in both CLI and MCP mode",
|
|
251
|
-
mcp: {
|
|
252
|
-
serverInfo: { name: "my-awesome-mcp-server", version: "1.0.0" },
|
|
253
|
-
},
|
|
254
|
-
})
|
|
255
|
-
// Define a tool that works everywhere
|
|
256
|
-
.addTool({
|
|
257
|
-
name: "greet",
|
|
258
|
-
description: "A tool to greet someone",
|
|
259
|
-
flags: [
|
|
260
|
-
{
|
|
261
|
-
name: "name",
|
|
262
|
-
type: "string",
|
|
263
|
-
mandatory: true,
|
|
264
|
-
options: ["--name"],
|
|
265
|
-
description: "Name to greet",
|
|
266
|
-
},
|
|
267
|
-
{
|
|
268
|
-
name: "style",
|
|
269
|
-
type: "string",
|
|
270
|
-
enum: ["formal", "casual"],
|
|
271
|
-
defaultValue: "casual",
|
|
272
|
-
description: "Greeting style",
|
|
273
|
-
},
|
|
274
|
-
],
|
|
275
|
-
// Optional: Define output schema for MCP clients (Claude Desktop, etc.)
|
|
276
|
-
// This only affects MCP mode - CLI mode works the same regardless
|
|
277
|
-
outputSchema: {
|
|
278
|
-
success: z.boolean().describe("Whether the greeting was successful"),
|
|
279
|
-
greeting: z.string().describe("The formatted greeting message"),
|
|
280
|
-
name: z.string().describe("The name that was greeted"),
|
|
281
|
-
},
|
|
282
|
-
handler: async (ctx) => {
|
|
283
|
-
// Use console.log freely - it's automatically safe in MCP mode!
|
|
284
|
-
console.log(`Greeting ${ctx.args.name} in a ${ctx.args.style} style...`);
|
|
285
|
-
|
|
286
|
-
const greeting =
|
|
287
|
-
ctx.args.style === "formal"
|
|
288
|
-
? `Good day, ${ctx.args.name}.`
|
|
289
|
-
: `Hey ${ctx.args.name}!`;
|
|
290
|
-
|
|
291
|
-
console.log(greeting);
|
|
292
|
-
return { success: true, greeting, name: ctx.args.name };
|
|
293
|
-
},
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
// parse() is async and works with both sync and async handlers
|
|
297
|
-
async function main() {
|
|
298
|
-
try {
|
|
299
|
-
await cli.parse(process.argv.slice(2));
|
|
300
|
-
} catch (error) {
|
|
301
|
-
console.error("Error:", error.message);
|
|
302
|
-
process.exit(1);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
main();
|
|
307
|
-
|
|
308
|
-
// Export if you want to test, use the CLI programmatically
|
|
309
|
-
// or use the --s-enable-fuzzing system flag to run fuzzy tests on your CLI
|
|
310
|
-
export default cli;
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
### MCP Tool Name Constraints
|
|
314
|
-
|
|
315
|
-
When using `.addTool()` or `.addMcpTool()`, tool names are automatically sanitized for MCP compatibility. MCP tool names must follow the pattern `^[a-zA-Z0-9_-]{1,64}$` (only alphanumeric characters, underscores, and hyphens, with a maximum length of 64 characters).
|
|
316
|
-
|
|
317
|
-
```typescript
|
|
318
|
-
// These names will be automatically sanitized:
|
|
319
|
-
cli.addTool({
|
|
320
|
-
name: "test.tool", // → "test_tool"
|
|
321
|
-
// ... rest of config
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
cli.addTool({
|
|
325
|
-
name: "my@tool", // → "my_tool"
|
|
326
|
-
// ... rest of config
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
cli.addTool({
|
|
330
|
-
name: "tool with spaces", // → "tool_with_spaces"
|
|
331
|
-
// ... rest of config
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
cli.addTool({
|
|
335
|
-
name: "very-long-tool-name-that-exceeds-the-64-character-limit-for-mcp", // → truncated to 64 chars
|
|
336
|
-
// ... rest of config
|
|
337
|
-
});
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
The library will warn you when tool names are sanitized, but your tools will continue to work normally. For CLI usage, the original name is preserved as the subcommand name.
|
|
341
|
-
|
|
342
|
-
## How to Run It
|
|
343
|
-
|
|
344
|
-
```bash
|
|
345
|
-
# This assumes `mycli` is your CLI's entry point
|
|
346
|
-
|
|
347
|
-
# 1. As a standard CLI subcommand
|
|
348
|
-
mycli greet --name Jane --style formal
|
|
349
|
-
|
|
350
|
-
# 2. As an MCP server, exposing the 'greet' tool
|
|
351
|
-
mycli --s-mcp-serve
|
|
352
|
-
|
|
353
|
-
# 3. Generate a DXT package for Claude Desktop (2-steps)
|
|
354
|
-
mycli --s-build-dxt ./my-dxt-package
|
|
355
|
-
|
|
356
|
-
# If you use ML models or packages that include binaries such as Sqlite3 or sharp, etc...
|
|
357
|
-
# You need to bundle the node_modules folder with your DXT package
|
|
358
|
-
# In order to do this, you need to use the following flag:
|
|
359
|
-
# First hard-install all the packages
|
|
360
|
-
rm -rf node_moduels
|
|
361
|
-
pnpm install --prod --node-linker=hoisted
|
|
362
|
-
# Then bundle with node_modules
|
|
363
|
-
mycli --s-build-dxt ./my-dxt-package --s-with-node-modules
|
|
364
|
-
# then packages the dxt
|
|
365
|
-
npx @anthropic-ai/dxt pack ./my-dxt-package
|
|
366
|
-
# then upload the dxt bundle to Claude Desktop from the settings > extensions > advanced screen
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
Read more on generating the DXT package here: [Generating DXT Packages](#generating-dxt-packages---s-build-dxt)
|
|
370
|
-
|
|
371
|
-
### Setting Up System-Wide CLI Access
|
|
372
|
-
|
|
373
|
-
To make your CLI available system-wide as a binary command, you need to configure the `bin` field in your `package.json` and use package linking:
|
|
374
|
-
|
|
375
|
-
**1. Configure your package.json:**
|
|
376
|
-
|
|
377
|
-
```json
|
|
378
|
-
{
|
|
379
|
-
"name": "my-cli-app",
|
|
380
|
-
"version": "1.0.0",
|
|
381
|
-
"type": "module",
|
|
382
|
-
"bin": {
|
|
383
|
-
"mycli": "./cli.js"
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
**2. Make your CLI file executable:**
|
|
389
|
-
|
|
390
|
-
```bash
|
|
391
|
-
chmod +x cli.js
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
**3. Add a shebang to your CLI file:**
|
|
395
|
-
|
|
396
|
-
```javascript
|
|
397
|
-
#!/usr/bin/env node
|
|
398
|
-
# or #!/usr/bin/env bun for native typescript runtime
|
|
399
|
-
|
|
400
|
-
import { ArgParser } from '@alcyone-labs/arg-parser';
|
|
401
|
-
|
|
402
|
-
const cli = ArgParser.withMcp({
|
|
403
|
-
appName: "My CLI",
|
|
404
|
-
appCommandName: "mycli",
|
|
405
|
-
// ... your configuration
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
// Parse command line arguments
|
|
409
|
-
await cli.parse(process.argv.slice(2));
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
**4. Link the package globally:**
|
|
413
|
-
|
|
414
|
-
```bash
|
|
415
|
-
# Using npm
|
|
416
|
-
npm link
|
|
417
|
-
|
|
418
|
-
# Using pnpm
|
|
419
|
-
pnpm link --global
|
|
420
|
-
|
|
421
|
-
# Using bun
|
|
422
|
-
bun link
|
|
423
|
-
|
|
424
|
-
# Using yarn
|
|
425
|
-
yarn link
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
**5. Use your CLI from anywhere:**
|
|
429
|
-
|
|
430
|
-
```bash
|
|
431
|
-
# Now you can run your CLI from any directory
|
|
432
|
-
mycli --help
|
|
433
|
-
mycli greet --name "World"
|
|
434
|
-
|
|
435
|
-
# Or use with npx/pnpx if you prefer
|
|
436
|
-
npx mycli --help
|
|
437
|
-
pnpx mycli greet --name "World"
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
**To unlink later:**
|
|
441
|
-
|
|
442
|
-
```bash
|
|
443
|
-
# Using npm
|
|
444
|
-
npm unlink --global my-cli-app
|
|
445
|
-
|
|
446
|
-
# Using pnpm
|
|
447
|
-
pnpm unlink --global
|
|
448
|
-
|
|
449
|
-
# Using bun
|
|
450
|
-
bun unlink
|
|
451
|
-
|
|
452
|
-
# Using yarn
|
|
453
|
-
yarn unlink
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
---
|
|
457
|
-
|
|
458
|
-
## Parsing Command-Line Arguments
|
|
459
|
-
|
|
460
|
-
ArgParser's `parse()` method is async and automatically handles both synchronous and asynchronous handlers:
|
|
461
|
-
|
|
462
|
-
### Auto-Execution versus Import: No More Boilerplate
|
|
463
|
-
|
|
464
|
-
ArgParser now provides auto-execution ability that eliminates the need for boilerplate code to check if your script is being run directly vs. imported. This enables use cases such as programmatically loading the CLI and scanning for tools or testing it from another script via the --s-enable-fuzzy flag or your own script.
|
|
465
|
-
|
|
466
|
-
```typescript
|
|
467
|
-
const cli = ArgParser.withMcp({
|
|
468
|
-
appName: "My CLI",
|
|
469
|
-
appCommandName: "my-cli",
|
|
470
|
-
handler: async (ctx) => ({ success: true, data: ctx.args }),
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
// Now, this will NOT automatically execute the parser if the script is imported, but will execute if called directly:
|
|
474
|
-
await cli
|
|
475
|
-
.parse(undefined, {
|
|
476
|
-
importMetaUrl: import.meta.url,
|
|
477
|
-
})
|
|
478
|
-
.catch(handleError);
|
|
479
|
-
|
|
480
|
-
// Or, using the manual APIs:
|
|
481
|
-
await cli.parseIfExecutedDirectly(import.meta.url).catch((error) => {
|
|
482
|
-
console.error(
|
|
483
|
-
"Fatal error:",
|
|
484
|
-
error instanceof Error ? error.message : String(error),
|
|
485
|
-
);
|
|
486
|
-
process.exit(1);
|
|
487
|
-
});
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
**Replaces previously confusing patterns:**
|
|
491
|
-
|
|
492
|
-
```typescript
|
|
493
|
-
// Brittle and breaks in sandboxes
|
|
494
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
495
|
-
await cli.parse().catch(handleError);
|
|
496
|
-
}
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
### Cannonical Usage Pattern
|
|
500
|
-
|
|
501
|
-
```typescript
|
|
502
|
-
const cli = ArgParser.withMcp({
|
|
503
|
-
appName: "My CLI",
|
|
504
|
-
handler: async (ctx) => {
|
|
505
|
-
// Works with both sync and async operations
|
|
506
|
-
const result = await someAsyncOperation(ctx.args.input);
|
|
507
|
-
return { success: true, result };
|
|
508
|
-
},
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
// parse() is async and works with both sync and async handlers
|
|
512
|
-
async function main() {
|
|
513
|
-
try {
|
|
514
|
-
// Option 1: Auto-detection - convenient for simple scripts
|
|
515
|
-
const result = await cli.parse();
|
|
516
|
-
|
|
517
|
-
// Option 2: Explicit arguments - full control
|
|
518
|
-
// const result = await cli.parse(process.argv.slice(2));
|
|
519
|
-
|
|
520
|
-
// Handler results are automatically awaited and merged
|
|
521
|
-
console.log(result.success); // true
|
|
522
|
-
} catch (error) {
|
|
523
|
-
console.error("Error:", error.message);
|
|
524
|
-
process.exit(1);
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
### Top-level await
|
|
530
|
-
|
|
531
|
-
Works in ES modules or Node.js >=18 with top-level await
|
|
532
|
-
|
|
533
|
-
```javascript
|
|
534
|
-
try {
|
|
535
|
-
// Auto-detection approach (recommended for simple scripts)
|
|
536
|
-
const result = await cli.parse();
|
|
537
|
-
|
|
538
|
-
// Or explicit approach for full control
|
|
539
|
-
// const result = await cli.parse(process.argv.slice(2));
|
|
540
|
-
|
|
541
|
-
console.log("Success:", result);
|
|
542
|
-
} catch (error) {
|
|
543
|
-
console.error("Error:", error.message);
|
|
544
|
-
process.exit(1);
|
|
545
|
-
}
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
### Promise-based parsing
|
|
549
|
-
|
|
550
|
-
If you need synchronous contexts, you can simply rely on promise-based APIs
|
|
551
|
-
|
|
552
|
-
```javascript
|
|
553
|
-
// Auto-detection approach
|
|
554
|
-
cli
|
|
555
|
-
.parse()
|
|
556
|
-
.then((result) => {
|
|
557
|
-
console.log("Success:", result);
|
|
558
|
-
})
|
|
559
|
-
.catch((error) => {
|
|
560
|
-
console.error("Error:", error.message);
|
|
561
|
-
process.exit(1);
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
// Or explicit approach
|
|
565
|
-
// cli
|
|
566
|
-
// .parse(process.argv.slice(2))
|
|
567
|
-
// .then((result) => {
|
|
568
|
-
// console.log("Success:", result);
|
|
569
|
-
// })
|
|
570
|
-
// .catch((error) => {
|
|
571
|
-
// console.error("Error:", error.message);
|
|
572
|
-
// process.exit(1);
|
|
573
|
-
// });
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
---
|
|
577
|
-
|
|
578
|
-
## Migrating from v1.x to the v2.0 `addTool` API
|
|
579
|
-
|
|
580
|
-
Version 2.0 introduces the `addTool()` method to unify CLI subcommand and MCP tool creation. This simplifies development by removing boilerplate and conditional logic.
|
|
581
|
-
|
|
582
|
-
### Before v2.0: Separate Definitions
|
|
583
|
-
|
|
584
|
-
Previously, you had to define CLI handlers and MCP tools separately, often with conditional logic inside the handler to manage different output formats.
|
|
585
|
-
|
|
586
|
-
```javascript
|
|
587
|
-
const cli = ArgParser.withMcp({
|
|
588
|
-
appName: "My Awesome CLI",
|
|
589
|
-
appCommandName: "mycli",
|
|
590
|
-
description: "A tool that works in both CLI and MCP mode",
|
|
591
|
-
mcp: {
|
|
592
|
-
serverInfo: { name: "my-awesome-mcp-server", version: "1.0.0" },
|
|
593
|
-
},
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
// Old way: Separate CLI subcommands and MCP tools
|
|
597
|
-
cli
|
|
598
|
-
.addSubCommand({
|
|
599
|
-
name: "search",
|
|
600
|
-
handler: async (ctx) => {
|
|
601
|
-
// Manual MCP detection was required
|
|
602
|
-
if (ctx.isMcp) {
|
|
603
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
604
|
-
} else {
|
|
605
|
-
console.log("Search results...");
|
|
606
|
-
return result;
|
|
607
|
-
}
|
|
608
|
-
},
|
|
609
|
-
})
|
|
610
|
-
// And a separate command to start the server
|
|
611
|
-
.addMcpSubCommand("serve", {
|
|
612
|
-
/* MCP config */
|
|
613
|
-
});
|
|
614
|
-
```
|
|
615
|
-
|
|
616
|
-
### After v2.0: The Unified `addTool()` Method
|
|
617
|
-
|
|
618
|
-
Now, a single `addTool()` definition creates both the CLI subcommand and the MCP tool. Console output is automatically managed, flags are converted to MCP schemas, and the server is started with a universal system flag.
|
|
619
|
-
|
|
620
|
-
```javascript
|
|
621
43
|
const cli = ArgParser.withMcp({
|
|
622
44
|
appName: "My Awesome CLI",
|
|
623
45
|
appCommandName: "mycli",
|
|
@@ -625,2159 +47,64 @@ const cli = ArgParser.withMcp({
|
|
|
625
47
|
mcp: {
|
|
626
48
|
serverInfo: { name: "my-awesome-mcp-server", version: "1.0.0" },
|
|
627
49
|
},
|
|
628
|
-
})
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
cli.addTool({
|
|
632
|
-
name: "search",
|
|
633
|
-
description: "Search for items",
|
|
50
|
+
}).addTool({
|
|
51
|
+
name: "greet",
|
|
52
|
+
description: "A tool to greet someone",
|
|
634
53
|
flags: [
|
|
635
|
-
{ name: "
|
|
636
|
-
{ name: "apiKey", type: "string", env: "API_KEY" }, // Universal Env support (also used for DXT)
|
|
54
|
+
{ name: "name", type: "string", mandatory: true, options: ["--name"] },
|
|
637
55
|
],
|
|
638
56
|
handler: async (ctx) => {
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
const results = await performSearch(ctx.args.query, ctx.args.apiKey);
|
|
642
|
-
console.log(`Found ${results.length} results`);
|
|
643
|
-
return { success: true, results };
|
|
57
|
+
console.log(`Hey ${ctx.args.name}!`);
|
|
58
|
+
return { success: true, greeting: `Hey ${ctx.args.name}!` };
|
|
644
59
|
},
|
|
645
60
|
});
|
|
646
61
|
|
|
647
|
-
|
|
648
|
-
// MCP usage: mycli --s-mcp-serve
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
**Benefits of Migrating:**
|
|
652
|
-
|
|
653
|
-
- **Less Code**: A single definition replaces two or more complex ones.
|
|
654
|
-
- **Simpler Logic**: No more manual MCP mode detection or response formatting.
|
|
655
|
-
- **Automatic Schemas**: Flags are automatically converted into the `input_schema` for MCP tools.
|
|
656
|
-
- **Automatic Console Safety**: `console.log` is automatically redirected in MCP mode.
|
|
657
|
-
- **Optional Output Schemas**: Add `outputSchema` only if you want structured responses for MCP clients - CLI mode works perfectly without them.
|
|
658
|
-
|
|
659
|
-
---
|
|
660
|
-
|
|
661
|
-
## Core Concepts
|
|
662
|
-
|
|
663
|
-
### Defining Flags
|
|
664
|
-
|
|
665
|
-
Flags are defined using the `IFlag` interface within the `flags` array of a tool or command.
|
|
666
|
-
|
|
667
|
-
```typescript
|
|
668
|
-
interface IFlag {
|
|
669
|
-
name: string; // Internal name (e.g., 'verbose')
|
|
670
|
-
options: string[]; // Command-line options (e.g., ['--verbose', '-v'])
|
|
671
|
-
type:
|
|
672
|
-
| "string"
|
|
673
|
-
| "number"
|
|
674
|
-
| "boolean"
|
|
675
|
-
| "array"
|
|
676
|
-
| "object"
|
|
677
|
-
| Function
|
|
678
|
-
| ZodSchema;
|
|
679
|
-
description?: string; // Help text
|
|
680
|
-
mandatory?: boolean | ((args: any) => boolean); // Whether the flag is required
|
|
681
|
-
defaultValue?: any; // Default value if not provided
|
|
682
|
-
flagOnly?: boolean; // A flag that doesn't consume a value (like --help)
|
|
683
|
-
enum?: any[]; // An array of allowed values
|
|
684
|
-
validate?: (value: any, parsedArgs?: any) => boolean | string | void; // Custom validation function
|
|
685
|
-
allowMultiple?: boolean; // Allow the flag to be provided multiple times
|
|
686
|
-
env?: string | string[]; // Maps flag to environment variable(s). Logic: Fallback (Env -> Flag) and Sync (Flag -> Env). Precedence: Flag > Env > Default.
|
|
687
|
-
positional?: number; // Captures Nth trailing positional argument (1-indexed). See Positional Arguments section.
|
|
688
|
-
dxtOptions?: DxtOptions; // Customizes how this flag appears in DXT package user_config
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
interface DxtOptions {
|
|
692
|
-
type?: "string" | "directory" | "file" | "boolean" | "number"; // UI input type in Claude Desktop
|
|
693
|
-
title?: string; // Display name in Claude Desktop (defaults to formatted flag name)
|
|
694
|
-
sensitive?: boolean; // Whether to hide the value in UI (defaults to true for security)
|
|
695
|
-
default?: any; // Default value for the user_config entry
|
|
696
|
-
min?: number; // Minimum value (for number types)
|
|
697
|
-
max?: number; // Maximum value (for number types)
|
|
698
|
-
}
|
|
699
|
-
```
|
|
700
|
-
|
|
701
|
-
### Environment Variable Support
|
|
702
|
-
|
|
703
|
-
ArgParser provides universal support for environment variables across all commands.
|
|
704
|
-
|
|
705
|
-
**Features:**
|
|
706
|
-
|
|
707
|
-
1. **Automatic Fallback**: If a flag is not provided via CLI, ArgParser looks for configured environment variables.
|
|
708
|
-
2. **Priority Handling**: `CLI Flag` > `Environment Variable` > `Default Value`.
|
|
709
|
-
3. **Reverse Sync**: Once a flag value is resolved (whether from CLI or Env), it is automatically written back to `process.env`. This ensures downstream code accessing `process.env` sees the consistent, final value.
|
|
710
|
-
4. **Array Support**: You can specify multiple env vars for a single flag; the first one found is used.
|
|
711
|
-
|
|
712
|
-
**Example:**
|
|
713
|
-
|
|
714
|
-
```typescript
|
|
715
|
-
parser.addFlag({
|
|
716
|
-
name: "apiKey",
|
|
717
|
-
type: "string",
|
|
718
|
-
env: ["MY_APP_API_KEY", "LEGACY_API_KEY"], // First match wins
|
|
719
|
-
defaultValue: "dev-key",
|
|
720
|
-
});
|
|
721
|
-
```
|
|
722
|
-
|
|
723
|
-
- If passed `--api-key val`: `apiKey` is "val", and `process.env.MY_APP_API_KEY` becomes "val".
|
|
724
|
-
- If not passed, but `MY_APP_API_KEY` exists: `apiKey` uses the env value.
|
|
725
|
-
- If neither: `apiKey` is "dev-key", and `process.env.MY_APP_API_KEY` is set to "dev-key".
|
|
726
|
-
|
|
727
|
-
### DXT Package User Configuration & Path Handling
|
|
728
|
-
|
|
729
|
-
ArgParser v2.5.0 introduces comprehensive DXT (Desktop Extension Toolkit) support with rich user interfaces, automatic path resolution, and context-aware development tools.
|
|
730
|
-
|
|
731
|
-
#### Enhanced dxtOptions
|
|
732
|
-
|
|
733
|
-
When generating DXT packages with `--s-build-dxt`, you can create rich user configuration interfaces using `dxtOptions`:
|
|
734
|
-
|
|
735
|
-
```typescript
|
|
736
|
-
import { ArgParser, DxtPathResolver } from "@alcyone-labs/arg-parser";
|
|
737
|
-
|
|
738
|
-
const parser = new ArgParser()
|
|
739
|
-
.withMcp({
|
|
740
|
-
name: "file-processor",
|
|
741
|
-
version: "1.0.0",
|
|
742
|
-
logPath: "${HOME}/logs/file-processor.log", // DXT variables supported!
|
|
743
|
-
})
|
|
744
|
-
.addFlag({
|
|
745
|
-
name: "input-file",
|
|
746
|
-
description: "File to process",
|
|
747
|
-
type: "string",
|
|
748
|
-
mandatory: true,
|
|
749
|
-
dxtOptions: {
|
|
750
|
-
type: "file",
|
|
751
|
-
title: "Select Input File",
|
|
752
|
-
},
|
|
753
|
-
})
|
|
754
|
-
.addFlag({
|
|
755
|
-
name: "output-dir",
|
|
756
|
-
description: "Output directory for processed files",
|
|
757
|
-
type: "string",
|
|
758
|
-
dxtOptions: {
|
|
759
|
-
type: "directory",
|
|
760
|
-
localDefault: "${DOCUMENTS}/processed-files", // Smart defaults with DXT variables
|
|
761
|
-
title: "Output Directory",
|
|
762
|
-
},
|
|
763
|
-
})
|
|
764
|
-
.addFlag({
|
|
765
|
-
name: "api-key",
|
|
766
|
-
description: "API authentication key",
|
|
767
|
-
type: "string",
|
|
768
|
-
env: "API_KEY",
|
|
769
|
-
dxtOptions: {
|
|
770
|
-
type: "string",
|
|
771
|
-
sensitive: true, // Excluded from DXT manifest for security
|
|
772
|
-
title: "API Key",
|
|
773
|
-
},
|
|
774
|
-
})
|
|
775
|
-
.addFlag({
|
|
776
|
-
name: "quality",
|
|
777
|
-
description: "Processing quality (1-100)",
|
|
778
|
-
type: "number",
|
|
779
|
-
dxtOptions: {
|
|
780
|
-
type: "number",
|
|
781
|
-
min: 1,
|
|
782
|
-
max: 100,
|
|
783
|
-
localDefault: 85,
|
|
784
|
-
title: "Quality (%)",
|
|
785
|
-
},
|
|
786
|
-
})
|
|
787
|
-
.addFlag({
|
|
788
|
-
name: "parallel",
|
|
789
|
-
description: "Enable parallel processing",
|
|
790
|
-
type: "boolean",
|
|
791
|
-
dxtOptions: {
|
|
792
|
-
type: "boolean",
|
|
793
|
-
localDefault: true,
|
|
794
|
-
title: "Parallel Processing",
|
|
795
|
-
},
|
|
796
|
-
});
|
|
62
|
+
await cli.parse();
|
|
797
63
|
```
|
|
798
64
|
|
|
799
|
-
|
|
65
|
+
## Documentation
|
|
800
66
|
|
|
801
|
-
|
|
67
|
+
For detailed information, please refer to the following guides:
|
|
802
68
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
// Helper functions for common patterns
|
|
810
|
-
const userDataPath = DxtPathResolver.createUserDataPath("cache.db");
|
|
811
|
-
const tempPath = DxtPathResolver.createTempPath("processing.tmp");
|
|
812
|
-
const configPath = DxtPathResolver.createConfigPath("settings.json");
|
|
813
|
-
|
|
814
|
-
// Context detection
|
|
815
|
-
const context = DxtPathResolver.detectContext();
|
|
816
|
-
if (context.isDxt) {
|
|
817
|
-
console.log("Running in DXT environment");
|
|
818
|
-
} else {
|
|
819
|
-
console.log("Running in development");
|
|
820
|
-
}
|
|
821
|
-
```
|
|
822
|
-
|
|
823
|
-
**Supported DXT Variables:**
|
|
824
|
-
|
|
825
|
-
- `${HOME}` - User's home directory
|
|
826
|
-
- `${DOCUMENTS}` - Documents folder
|
|
827
|
-
- `${DOWNLOADS}` - Downloads folder
|
|
828
|
-
- `${DESKTOP}` - Desktop folder
|
|
829
|
-
- `${__dirname}` - Entry point directory (DXT package root in DXT)
|
|
830
|
-
- `${pathSeparator}` - Platform-specific path separator
|
|
831
|
-
- `${DXT_DIR}` - DXT package directory (DXT only)
|
|
832
|
-
- `${EXTENSION_DIR}` - Extension root directory (DXT only)
|
|
833
|
-
|
|
834
|
-
#### dxtOptions Properties
|
|
835
|
-
|
|
836
|
-
| Property | Type | Description |
|
|
837
|
-
| -------------- | ------------------------------------------------------------ | ------------------------------------------------ |
|
|
838
|
-
| `type` | `'string' \| 'file' \| 'directory' \| 'boolean' \| 'number'` | UI component type |
|
|
839
|
-
| `sensitive` | `boolean` | Mark as sensitive (excluded from manifest) |
|
|
840
|
-
| `localDefault` | `string \| number \| boolean` | Default for development (supports DXT variables) |
|
|
841
|
-
| `multiple` | `boolean` | Allow multiple values |
|
|
842
|
-
| `min` / `max` | `number` | Validation constraints |
|
|
843
|
-
| `title` | `string` | Custom display name |
|
|
844
|
-
|
|
845
|
-
#### Security & Best Practices
|
|
846
|
-
|
|
847
|
-
- **Sensitive Data**: Use `sensitive: true` for passwords, API keys, tokens
|
|
848
|
-
- **Smart Defaults**: Use DXT variables in `localDefault` for portable paths
|
|
849
|
-
- **Type Safety**: Match `dxtOptions.type` with flag `type` for validation
|
|
850
|
-
- **Cross-Platform**: Use `${pathSeparator}` for platform-independent paths
|
|
851
|
-
|
|
852
|
-
#### Comprehensive Documentation
|
|
853
|
-
|
|
854
|
-
For detailed guides and examples:
|
|
855
|
-
|
|
856
|
-
- **[DXT Path Handling Guide](./docs/DXT_PATH_HANDLING.md)** - Complete path resolution guide
|
|
857
|
-
- **[dxtOptions API Documentation](./docs/DXT_OPTIONS_API.md)** - Full API reference with examples
|
|
858
|
-
- **[DXT Migration Guide](./docs/DXT_MIGRATION.md)** - Migrate existing applications
|
|
859
|
-
- **[DXT Practical Examples](./docs/DXT_EXAMPLES.md)** - Real-world usage patterns
|
|
860
|
-
|
|
861
|
-
### Type Handling and Validation
|
|
862
|
-
|
|
863
|
-
ArgParser provides **strong typing** for flag definitions with comprehensive validation at both compile-time and runtime. The `type` property accepts multiple formats and ensures type safety throughout your application.
|
|
864
|
-
|
|
865
|
-
#### Supported Type Formats
|
|
866
|
-
|
|
867
|
-
You can define flag types using either **constructor functions** or **string literals**:
|
|
868
|
-
|
|
869
|
-
```typescript
|
|
870
|
-
const parser = new ArgParser({
|
|
871
|
-
/* ... */
|
|
872
|
-
}).addFlags([
|
|
873
|
-
// Constructor functions (recommended for TypeScript)
|
|
874
|
-
{ name: "count", options: ["--count"], type: Number },
|
|
875
|
-
{ name: "enabled", options: ["--enabled"], type: Boolean, flagOnly: true },
|
|
876
|
-
{ name: "files", options: ["--files"], type: Array, allowMultiple: true },
|
|
877
|
-
|
|
878
|
-
// String literals (case-insensitive)
|
|
879
|
-
{ name: "name", options: ["--name"], type: "string" },
|
|
880
|
-
{ name: "port", options: ["--port"], type: "number" },
|
|
881
|
-
{ name: "verbose", options: ["-v"], type: "boolean", flagOnly: true },
|
|
882
|
-
{ name: "tags", options: ["--tags"], type: "array", allowMultiple: true },
|
|
883
|
-
{ name: "config", options: ["--config"], type: "object" },
|
|
884
|
-
|
|
885
|
-
// Custom parser functions (sync)
|
|
886
|
-
{
|
|
887
|
-
name: "date",
|
|
888
|
-
options: ["--date"],
|
|
889
|
-
type: (value: string) => new Date(value),
|
|
890
|
-
},
|
|
891
|
-
|
|
892
|
-
// Async custom parser functions
|
|
893
|
-
{
|
|
894
|
-
name: "config",
|
|
895
|
-
options: ["--config"],
|
|
896
|
-
type: async (filePath: string) => {
|
|
897
|
-
const content = await fs.readFile(filePath, "utf8");
|
|
898
|
-
return JSON.parse(content);
|
|
899
|
-
},
|
|
900
|
-
},
|
|
901
|
-
{
|
|
902
|
-
name: "user",
|
|
903
|
-
options: ["--user-id"],
|
|
904
|
-
type: async (userId: string) => {
|
|
905
|
-
const response = await fetch(`/api/users/${userId}`);
|
|
906
|
-
return response.json();
|
|
907
|
-
},
|
|
908
|
-
},
|
|
909
|
-
]);
|
|
910
|
-
```
|
|
911
|
-
|
|
912
|
-
#### Runtime Type Validation
|
|
913
|
-
|
|
914
|
-
The type system validates flag definitions at runtime and throws descriptive errors for invalid configurations:
|
|
915
|
-
|
|
916
|
-
```typescript
|
|
917
|
-
// ✅ Valid - these work
|
|
918
|
-
{ name: "count", options: ["--count"], type: Number }
|
|
919
|
-
{ name: "count", options: ["--count"], type: "number" }
|
|
920
|
-
{ name: "count", options: ["--count"], type: "NUMBER" } // case-insensitive
|
|
921
|
-
|
|
922
|
-
// ❌ Invalid - these throw ZodError
|
|
923
|
-
{ name: "count", options: ["--count"], type: "invalid-type" }
|
|
924
|
-
{ name: "count", options: ["--count"], type: 42 } // primitive instead of constructor
|
|
925
|
-
{ name: "count", options: ["--count"], type: null }
|
|
926
|
-
```
|
|
69
|
+
- 📖 **[Core Concepts](./docs/CORE_CONCEPTS.md)**: Flag definitions, type handling, validation, and positional arguments.
|
|
70
|
+
- 🤖 **[MCP & Claude Desktop Integration](./docs/MCP.md)**: Full guide on MCP servers, DXT bundling, and Claude integration.
|
|
71
|
+
- 🖥️ **[OpenTUI Reference](./docs/TUI.md)**: Building rich terminal interfaces with SolidJS.
|
|
72
|
+
- 📂 **[Working Directory Management](./docs/WORKING_DIRECTORY.md)**: Handling PWD and monorepos.
|
|
73
|
+
- 🚀 **[Migration Guide (v1 to v2)](./docs/MIGRATION_V2.md)**: Moving to the unified `addTool` API.
|
|
927
74
|
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
- **String literals** are automatically converted to constructor functions internally
|
|
931
|
-
- **Constructor functions** are preserved as-is
|
|
932
|
-
- **Custom parser functions** (sync and async) allow complex transformations
|
|
933
|
-
- **undefined** falls back to the default `"string"` type
|
|
934
|
-
|
|
935
|
-
#### Async Custom Parser Support
|
|
936
|
-
|
|
937
|
-
Custom parser functions can be **asynchronous**, enabling powerful use cases like file I/O, API calls, and database lookups:
|
|
938
|
-
|
|
939
|
-
```typescript
|
|
940
|
-
const parser = new ArgParser({
|
|
941
|
-
/* ... */
|
|
942
|
-
}).addFlags([
|
|
943
|
-
{
|
|
944
|
-
name: "config",
|
|
945
|
-
options: ["--config"],
|
|
946
|
-
type: async (filePath: string) => {
|
|
947
|
-
const content = await fs.readFile(filePath, "utf8");
|
|
948
|
-
return JSON.parse(content);
|
|
949
|
-
},
|
|
950
|
-
},
|
|
951
|
-
{
|
|
952
|
-
name: "user",
|
|
953
|
-
options: ["--user-id"],
|
|
954
|
-
type: async (userId: string) => {
|
|
955
|
-
const response = await fetch(`/api/users/${userId}`);
|
|
956
|
-
if (!response.ok) throw new Error(`User not found: ${userId}`);
|
|
957
|
-
return response.json();
|
|
958
|
-
},
|
|
959
|
-
},
|
|
960
|
-
]);
|
|
961
|
-
|
|
962
|
-
// Usage: --config ./settings.json --user-id 123
|
|
963
|
-
const result = await parser.parse(process.argv.slice(2));
|
|
964
|
-
// result.config contains parsed JSON from file
|
|
965
|
-
// result.user contains user data from API
|
|
966
|
-
```
|
|
967
|
-
|
|
968
|
-
**Key Features:**
|
|
969
|
-
|
|
970
|
-
- ✅ **Backward compatible** - existing sync parsers continue to work
|
|
971
|
-
- ✅ **Automatic detection** - no configuration needed, just return a Promise
|
|
972
|
-
- ✅ **Error handling** - async errors are properly propagated
|
|
973
|
-
- ✅ **Performance** - parsers run concurrently when possible
|
|
974
|
-
|
|
975
|
-
#### Type Conversion Examples
|
|
976
|
-
|
|
977
|
-
```typescript
|
|
978
|
-
// String flags
|
|
979
|
-
--name value → "value"
|
|
980
|
-
--name="quoted value" → "quoted value"
|
|
981
|
-
|
|
982
|
-
// Number flags
|
|
983
|
-
--count 42 → 42
|
|
984
|
-
--port=8080 → 8080
|
|
985
|
-
|
|
986
|
-
// Boolean flags (flagOnly: true)
|
|
987
|
-
--verbose → true
|
|
988
|
-
(no flag) → false
|
|
989
|
-
|
|
990
|
-
// Array flags (allowMultiple: true)
|
|
991
|
-
--tags tag1,tag2,tag3 → ["tag1", "tag2", "tag3"]
|
|
992
|
-
--file file1.txt --file file2.txt → ["file1.txt", "file2.txt"]
|
|
75
|
+
## How to Run It
|
|
993
76
|
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
77
|
+
```bash
|
|
78
|
+
# 1. As a standard CLI subcommand
|
|
79
|
+
mycli greet --name Jane
|
|
997
80
|
|
|
998
|
-
|
|
999
|
-
--
|
|
1000
|
-
--user-id "123" → user data from API (async)
|
|
81
|
+
# 2. As an MCP server
|
|
82
|
+
mycli --s-mcp-serve
|
|
1001
83
|
|
|
1002
|
-
|
|
1003
|
-
--
|
|
1004
|
-
--deployment '{"env":"prod","region":"us-east-1"}' → validated object
|
|
84
|
+
# 3. Generate a DXT package
|
|
85
|
+
mycli --s-build-dxt ./my-dxt-package
|
|
1005
86
|
```
|
|
1006
87
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
**Since v2.5.0** - You can now use Zod schemas as flag types for structured JSON input with automatic validation and proper MCP JSON Schema generation:
|
|
88
|
+
## OpenTUI: Reactive Rich Terminal Interfaces
|
|
1010
89
|
|
|
1011
|
-
|
|
1012
|
-
import { z } from "zod";
|
|
90
|
+
ArgParser includes **OpenTUI v2**, a reactive TUI framework built on SolidJS.
|
|
1013
91
|
|
|
1014
|
-
|
|
1015
|
-
host: z.string().describe("Database host address"),
|
|
1016
|
-
port: z.number().min(1).max(65535).describe("Database port number"),
|
|
1017
|
-
credentials: z.object({
|
|
1018
|
-
username: z.string().describe("Database username"),
|
|
1019
|
-
password: z.string().describe("Database password"),
|
|
1020
|
-
}),
|
|
1021
|
-
ssl: z.boolean().optional().describe("Enable SSL connection"),
|
|
1022
|
-
});
|
|
1023
|
-
|
|
1024
|
-
const cli = ArgParser.withMcp({
|
|
1025
|
-
appName: "Database CLI",
|
|
1026
|
-
appCommandName: "db-cli",
|
|
1027
|
-
}).addTool({
|
|
1028
|
-
name: "connect",
|
|
1029
|
-
description: "Connect to database with structured configuration",
|
|
1030
|
-
flags: [
|
|
1031
|
-
{
|
|
1032
|
-
name: "config",
|
|
1033
|
-
options: ["--config", "-c"],
|
|
1034
|
-
type: DatabaseConfigSchema, // 🎉 Zod schema as type!
|
|
1035
|
-
description: "Database configuration as JSON object",
|
|
1036
|
-
mandatory: true,
|
|
1037
|
-
},
|
|
1038
|
-
],
|
|
1039
|
-
handler: async (ctx) => {
|
|
1040
|
-
// ctx.args.config is fully typed and validated!
|
|
1041
|
-
const { host, port, credentials, ssl } = ctx.args.config;
|
|
1042
|
-
console.log(`Connecting to ${host}:${port} as ${credentials.username}`);
|
|
1043
|
-
return { success: true };
|
|
1044
|
-
},
|
|
1045
|
-
});
|
|
1046
|
-
|
|
1047
|
-
// CLI usage with JSON validation:
|
|
1048
|
-
// db-cli connect --config '{"host":"localhost","port":5432,"credentials":{"username":"admin","password":"secret"},"ssl":true}'
|
|
1049
|
-
|
|
1050
|
-
// MCP usage: Generates proper JSON Schema for MCP clients
|
|
1051
|
-
// db-cli --s-mcp-serve
|
|
1052
|
-
```
|
|
1053
|
-
|
|
1054
|
-
**Example with Complex Nested Schema:**
|
|
1055
|
-
|
|
1056
|
-
```typescript
|
|
1057
|
-
const DeploymentSchema = z.object({
|
|
1058
|
-
environment: z.enum(["dev", "staging", "prod"]),
|
|
1059
|
-
region: z.string(),
|
|
1060
|
-
scaling: z.object({
|
|
1061
|
-
minInstances: z.number().min(1),
|
|
1062
|
-
maxInstances: z.number().min(1),
|
|
1063
|
-
targetCpu: z.number().min(10).max(100),
|
|
1064
|
-
}),
|
|
1065
|
-
monitoring: z.object({
|
|
1066
|
-
enabled: z.boolean(),
|
|
1067
|
-
alertEmail: z.string().email().optional(),
|
|
1068
|
-
metrics: z.array(z.string()),
|
|
1069
|
-
}),
|
|
1070
|
-
});
|
|
1071
|
-
|
|
1072
|
-
// This generates comprehensive JSON Schema for MCP clients
|
|
1073
|
-
// while providing full validation and type safety for CLI usage
|
|
1074
|
-
```
|
|
1075
|
-
|
|
1076
|
-
### Hierarchical CLIs (Sub-Commands)
|
|
1077
|
-
|
|
1078
|
-
While `addTool()` is the recommended way to create subcommands that are also MCP-compatible, you can use `.addSubCommand()` for traditional CLI hierarchies.
|
|
1079
|
-
|
|
1080
|
-
> **Note**: By default, subcommands created with `.addSubCommand()` are exposed to MCP as tools. If you want to create CLI-only subcommands, set `includeSubCommands: false` when adding tools.
|
|
1081
|
-
|
|
1082
|
-
```typescript
|
|
1083
|
-
// Create a parser for a nested command
|
|
1084
|
-
const logsParser = new ArgParser().addFlags([
|
|
1085
|
-
{ name: "follow", options: ["-f"], type: "boolean", flagOnly: true },
|
|
1086
|
-
]);
|
|
1087
|
-
|
|
1088
|
-
// This creates a command group: `my-cli monitor`
|
|
1089
|
-
const monitorParser = new ArgParser().addSubCommand({
|
|
1090
|
-
name: "logs",
|
|
1091
|
-
description: "Show application logs",
|
|
1092
|
-
parser: logsParser,
|
|
1093
|
-
handler: ({ args }) => console.log(`Following logs: ${args.follow}`),
|
|
1094
|
-
});
|
|
1095
|
-
|
|
1096
|
-
// Attach the command group to the main CLI
|
|
1097
|
-
const cli = new ArgParser().addSubCommand({
|
|
1098
|
-
name: "monitor",
|
|
1099
|
-
description: "Monitoring commands",
|
|
1100
|
-
parser: monitorParser,
|
|
1101
|
-
});
|
|
1102
|
-
|
|
1103
|
-
// Usage: my-cli monitor logs -f
|
|
1104
|
-
```
|
|
1105
|
-
|
|
1106
|
-
#### MCP Exposure Control
|
|
1107
|
-
|
|
1108
|
-
```typescript
|
|
1109
|
-
// By default, subcommands are exposed to MCP
|
|
1110
|
-
const mcpTools = parser.toMcpTools(); // Includes all subcommands
|
|
1111
|
-
|
|
1112
|
-
// To exclude subcommands from MCP (CLI-only)
|
|
1113
|
-
const mcpToolsOnly = parser.toMcpTools({ includeSubCommands: false });
|
|
1114
|
-
|
|
1115
|
-
// Name conflicts: You cannot have both addSubCommand("name") and addTool({ name: "name" })
|
|
1116
|
-
// This will throw an error:
|
|
1117
|
-
parser.addSubCommand({ name: "process", parser: subParser });
|
|
1118
|
-
parser.addTool({ name: "process", handler: async () => {} }); // ❌ Error: Sub-command 'process' already exists
|
|
1119
|
-
```
|
|
1120
|
-
|
|
1121
|
-
### Flag Inheritance (`inheritParentFlags`)
|
|
1122
|
-
|
|
1123
|
-
ArgParser supports flag inheritance for CLI hierarchies. By default, sub-commands do not inherit flags from their parents. You can control this behavior using the `inheritParentFlags` option, using either a boolean (for basic/legacy behavior) or the `FlagInheritance` configuration object (for advanced control).
|
|
1124
|
-
|
|
1125
|
-
#### Basic Inheritance (Snapshot)
|
|
1126
|
-
|
|
1127
|
-
Set `inheritParentFlags: true` (or `FlagInheritance.DirectParentOnly`) to inherit flags from the _direct parent_ at the moment the sub-command is attached.
|
|
1128
|
-
|
|
1129
|
-
> **Note**: This is a snapshot of the parent's flags at the time `.addSubCommand()` is called. If the parent acquires new flags later (e.g., by inheriting from a grandparent), the child will NOT see them unless `FlagInheritance.AllParents` is used.
|
|
1130
|
-
|
|
1131
|
-
```typescript
|
|
1132
|
-
// Child inherits current flags from parent
|
|
1133
|
-
const childParser = new ArgParser({ inheritParentFlags: true });
|
|
1134
|
-
```
|
|
1135
|
-
|
|
1136
|
-
#### Full Chain Inheritance
|
|
1137
|
-
|
|
1138
|
-
For complex hierarchies (e.g. `root -> mid -> leaf`), especially when constructing parsers bottom-up, use `FlagInheritance.AllParents`. This ensures that flags propagate down the entire chain, even if the intermediate parent inherits them _after_ the leaf was attached.
|
|
1139
|
-
|
|
1140
|
-
```typescript
|
|
1141
|
-
import { ArgParser, FlagInheritance } from "@alcyone-labs/arg-parser";
|
|
1142
|
-
|
|
1143
|
-
const root = new ArgParser().addFlag({
|
|
1144
|
-
name: "root-flag",
|
|
1145
|
-
options: ["--root"],
|
|
1146
|
-
});
|
|
1147
|
-
const mid = new ArgParser({ inheritParentFlags: FlagInheritance.AllParents });
|
|
1148
|
-
const leaf = new ArgParser({ inheritParentFlags: FlagInheritance.AllParents });
|
|
1149
|
-
|
|
1150
|
-
// Even if you link bottom-up:
|
|
1151
|
-
mid.addSubCommand({ name: "leaf", parser: leaf });
|
|
1152
|
-
root.addSubCommand({ name: "mid", parser: mid });
|
|
1153
|
-
|
|
1154
|
-
// 'leaf' will correctly have 'root-flag' thanks to deep propagation
|
|
1155
|
-
```
|
|
1156
|
-
|
|
1157
|
-
#### Inheritance Options Reference
|
|
1158
|
-
|
|
1159
|
-
| Value | Legacy Boolean | Behavior |
|
|
1160
|
-
| ---------------------------------- | -------------- | ----------------------------------------------------------- |
|
|
1161
|
-
| `FlagInheritance.NONE` | `false` | No flags are inherited (Default) |
|
|
1162
|
-
| `FlagInheritance.DirectParentOnly` | `true` | Inherits from direct parent only (Snapshot) |
|
|
1163
|
-
| `FlagInheritance.AllParents` | N/A | Inherits from entire ancestor chain (Recursive Propagation) |
|
|
1164
|
-
|
|
1165
|
-
### Dynamic Flags (`dynamicRegister`)
|
|
1166
|
-
|
|
1167
|
-
Register flags at runtime from another flag's value (e.g., load a manifest and add flags programmatically). This works in normal runs and when showing `--help`.
|
|
1168
|
-
|
|
1169
|
-
- Two-phase parsing: loader flags run first, can register more flags, then parsing continues with the full set
|
|
1170
|
-
- Help preload: when `--help` is present, dynamic loaders run to show complete help (no command handlers execute)
|
|
1171
|
-
- Cleanup: dynamic flags are removed between parses (no accumulation)
|
|
1172
|
-
- Async-friendly: loaders can be async (e.g., `fs.readFile`)
|
|
1173
|
-
|
|
1174
|
-
```ts
|
|
1175
|
-
import { readFile } from "node:fs/promises";
|
|
1176
|
-
import { ArgParser } from "@alcyone-labs/arg-parser";
|
|
1177
|
-
|
|
1178
|
-
const cli = new ArgParser().addFlags([
|
|
1179
|
-
{
|
|
1180
|
-
name: "manifest",
|
|
1181
|
-
options: ["-w", "--manifest"],
|
|
1182
|
-
type: "string",
|
|
1183
|
-
description: "Path to manifest.json that defines extra flags",
|
|
1184
|
-
dynamicRegister: async ({ value, registerFlags }) => {
|
|
1185
|
-
const json = JSON.parse(await readFile(value, "utf8"));
|
|
1186
|
-
if (Array.isArray(json.flags)) {
|
|
1187
|
-
// Each entry should be a valid IFlag
|
|
1188
|
-
registerFlags(json.flags);
|
|
1189
|
-
}
|
|
1190
|
-
},
|
|
1191
|
-
},
|
|
1192
|
-
]);
|
|
1193
|
-
|
|
1194
|
-
// Examples:
|
|
1195
|
-
// my-cli -w manifest.json --help → help includes dynamic flags
|
|
1196
|
-
// my-cli -w manifest.json --foo bar → dynamic flag "--foo" parsed/validated normally
|
|
1197
|
-
```
|
|
1198
|
-
|
|
1199
|
-
Notes:
|
|
1200
|
-
|
|
1201
|
-
- Inherited behavior works normally: if loader lives on a parent parser and children use `inheritParentFlags`, dynamic flags will be visible to children
|
|
1202
|
-
- For heavy loaders, implement app-level caching inside your `dynamicRegister` (e.g., memoize by absolute path + mtime); library-level caching may be added later
|
|
1203
|
-
|
|
1204
|
-
### Positional Arguments
|
|
1205
|
-
|
|
1206
|
-
ArgParser supports positional (trailing) arguments for a more natural CLI syntax. Instead of requiring flags for every value, you can capture trailing arguments by position.
|
|
1207
|
-
|
|
1208
|
-
**Before:**
|
|
1209
|
-
```bash
|
|
1210
|
-
workflow show --id 8fadf090-xxx
|
|
1211
|
-
```
|
|
1212
|
-
|
|
1213
|
-
**After:**
|
|
1214
|
-
```bash
|
|
1215
|
-
workflow show 8fadf090-xxx
|
|
1216
|
-
```
|
|
1217
|
-
|
|
1218
|
-
#### Basic Usage
|
|
1219
|
-
|
|
1220
|
-
Add the `positional` property to a flag definition. The value is 1-indexed (first trailing arg = 1, second = 2, etc.):
|
|
1221
|
-
|
|
1222
|
-
```typescript
|
|
1223
|
-
const cli = new ArgParser()
|
|
1224
|
-
.addFlag({
|
|
1225
|
-
name: "id",
|
|
1226
|
-
type: "string",
|
|
1227
|
-
mandatory: true,
|
|
1228
|
-
options: ["--id"], // Fallback syntax: --id <value>
|
|
1229
|
-
positional: 1, // Primary: captures first trailing arg
|
|
1230
|
-
description: "Resource ID to show",
|
|
1231
|
-
valueHint: "ID", // Used in help text: <ID>
|
|
1232
|
-
})
|
|
1233
|
-
.setHandler((ctx) => {
|
|
1234
|
-
console.log(`Showing: ${ctx.args.id}`);
|
|
1235
|
-
});
|
|
1236
|
-
|
|
1237
|
-
// Both work:
|
|
1238
|
-
// cli.parse(["abc123"]) → id = "abc123"
|
|
1239
|
-
// cli.parse(["--id", "abc123"]) → id = "abc123"
|
|
1240
|
-
```
|
|
1241
|
-
|
|
1242
|
-
#### Multiple Positional Arguments
|
|
1243
|
-
|
|
1244
|
-
Capture multiple trailing arguments using different positional indices:
|
|
1245
|
-
|
|
1246
|
-
```typescript
|
|
1247
|
-
const cli = new ArgParser().addFlags([
|
|
1248
|
-
{
|
|
1249
|
-
name: "source",
|
|
1250
|
-
type: "string",
|
|
1251
|
-
mandatory: true,
|
|
1252
|
-
options: ["--source", "-s"],
|
|
1253
|
-
positional: 1, // First trailing arg
|
|
1254
|
-
valueHint: "SOURCE",
|
|
1255
|
-
},
|
|
1256
|
-
{
|
|
1257
|
-
name: "dest",
|
|
1258
|
-
type: "string",
|
|
1259
|
-
mandatory: true,
|
|
1260
|
-
options: ["--dest", "-d"],
|
|
1261
|
-
positional: 2, // Second trailing arg
|
|
1262
|
-
valueHint: "DEST",
|
|
1263
|
-
},
|
|
1264
|
-
]);
|
|
1265
|
-
|
|
1266
|
-
// Usage: copy file.txt backup/
|
|
1267
|
-
// Result: source = "file.txt", dest = "backup/"
|
|
1268
|
-
```
|
|
1269
|
-
|
|
1270
|
-
#### Precedence Rules
|
|
1271
|
-
|
|
1272
|
-
- **Flag syntax takes priority**: If both `--flag value` AND a positional arg are provided, the flag value is used
|
|
1273
|
-
- **Either satisfies mandatory**: A mandatory flag is satisfied by EITHER positional or flag syntax
|
|
1274
|
-
- **Order matters**: Positional args are assigned in index order (1, 2, 3...)
|
|
1275
|
-
- **Type coercion applies**: Positional values go through the same type coercion as flag values
|
|
1276
|
-
|
|
1277
|
-
#### Help Text
|
|
1278
|
-
|
|
1279
|
-
When positional arguments are defined, help text automatically shows a usage pattern:
|
|
1280
|
-
|
|
1281
|
-
```
|
|
1282
|
-
Usage: workflow show [OPTIONS] <ID>
|
|
1283
|
-
|
|
1284
|
-
Flags:
|
|
1285
|
-
--id Resource ID to show
|
|
1286
|
-
Type: string
|
|
1287
|
-
Example: --id value
|
|
1288
|
-
Positional argument #1
|
|
1289
|
-
```
|
|
1290
|
-
|
|
1291
|
-
Mandatory positional args appear as `<NAME>`, optional as `[NAME]`.
|
|
1292
|
-
|
|
1293
|
-
### Automatic Help Display
|
|
1294
|
-
|
|
1295
|
-
ArgParser provides features to automatically show help messages when a command is invoked incorrectly or as a "container" command.
|
|
1296
|
-
|
|
1297
|
-
- **`ctx.displayHelp()`**: Programmatically trigger help for the current command from within its handler.
|
|
1298
|
-
- **`autoHelpHandler`**: A pre-built handler for container commands (e.g., `git remote`) that simply displays the help text.
|
|
1299
|
-
- **`triggerAutoHelpIfNoHandler`**: A setting that, when enabled, automatically triggers the help display for any command or sub-command that does not have an explicit handler defined.
|
|
1300
|
-
|
|
1301
|
-
For more details, see the [Automatic Help Display Guide](docs/DISPLAY_HELP.md) and the [example demo](examples/auto-help-demo.ts).
|
|
1302
|
-
|
|
1303
|
-
---
|
|
1304
|
-
|
|
1305
|
-
## MCP & Claude Desktop Integration
|
|
1306
|
-
|
|
1307
|
-
### Output Schema Support
|
|
1308
|
-
|
|
1309
|
-
Output schemas are **completely optional** and **only affect MCP mode** (Claude Desktop, MCP clients). They have **zero impact** on CLI usage - your CLI will work exactly the same with or without them.
|
|
1310
|
-
|
|
1311
|
-
**When do I need output schemas?**
|
|
1312
|
-
|
|
1313
|
-
- ❌ **CLI-only usage**: Never needed - skip this section entirely
|
|
1314
|
-
- ✅ **MCP integration**: Optional but recommended for better structured responses
|
|
1315
|
-
- ✅ **Claude Desktop**: Helpful for Claude to understand your tool's output format
|
|
1316
|
-
|
|
1317
|
-
**Key Points:**
|
|
1318
|
-
|
|
1319
|
-
- ✅ **CLI works perfectly without them**: Your command-line interface is unaffected
|
|
1320
|
-
- ✅ **MCP-only feature**: Only used when running with `--s-mcp-serve`
|
|
1321
|
-
- ✅ **Version-aware**: Automatically included only for compatible MCP clients (v2025-06-18+)
|
|
1322
|
-
- ✅ **Flexible**: Use predefined patterns or custom Zod schemas
|
|
1323
|
-
|
|
1324
|
-
#### Basic Usage
|
|
1325
|
-
|
|
1326
|
-
```typescript
|
|
1327
|
-
import { z } from "zod";
|
|
1328
|
-
|
|
1329
|
-
.addTool({
|
|
1330
|
-
name: "process-file",
|
|
1331
|
-
description: "Process a file",
|
|
1332
|
-
flags: [
|
|
1333
|
-
{ name: "path", options: ["--path"], type: "string", mandatory: true }
|
|
1334
|
-
],
|
|
1335
|
-
// Optional: Only needed if you want structured MCP responses
|
|
1336
|
-
// CLI mode works exactly the same whether this is present or not
|
|
1337
|
-
outputSchema: {
|
|
1338
|
-
success: z.boolean().describe("Whether processing succeeded"),
|
|
1339
|
-
filePath: z.string().describe("Path to the processed file"),
|
|
1340
|
-
size: z.number().describe("File size in bytes"),
|
|
1341
|
-
lastModified: z.string().describe("Last modification timestamp")
|
|
1342
|
-
},
|
|
1343
|
-
handler: async (ctx) => {
|
|
1344
|
-
// Your logic here - same code for both CLI and MCP
|
|
1345
|
-
// The outputSchema doesn't change how this function works
|
|
1346
|
-
return {
|
|
1347
|
-
success: true,
|
|
1348
|
-
filePath: ctx.args.path,
|
|
1349
|
-
size: 1024,
|
|
1350
|
-
lastModified: new Date().toISOString()
|
|
1351
|
-
};
|
|
1352
|
-
}
|
|
1353
|
-
})
|
|
1354
|
-
|
|
1355
|
-
// CLI usage (outputSchema ignored): mycli process-file --path /my/file.txt
|
|
1356
|
-
// MCP usage (outputSchema provides structure): mycli --s-mcp-serve
|
|
1357
|
-
```
|
|
1358
|
-
|
|
1359
|
-
#### Predefined Schema Patterns
|
|
1360
|
-
|
|
1361
|
-
For common use cases, use predefined patterns:
|
|
1362
|
-
|
|
1363
|
-
```typescript
|
|
1364
|
-
// For simple success/error responses
|
|
1365
|
-
outputSchema: "successError";
|
|
1366
|
-
|
|
1367
|
-
// For operations that return data
|
|
1368
|
-
outputSchema: "successWithData";
|
|
1369
|
-
|
|
1370
|
-
// For file operations
|
|
1371
|
-
outputSchema: "fileOperation";
|
|
1372
|
-
|
|
1373
|
-
// For process execution
|
|
1374
|
-
outputSchema: "processExecution";
|
|
1375
|
-
|
|
1376
|
-
// For list operations
|
|
1377
|
-
outputSchema: "list";
|
|
1378
|
-
```
|
|
1379
|
-
|
|
1380
|
-
#### Custom Zod Schemas
|
|
1381
|
-
|
|
1382
|
-
For complex data structures:
|
|
1383
|
-
|
|
1384
|
-
```typescript
|
|
1385
|
-
outputSchema: z.object({
|
|
1386
|
-
analysis: z.object({
|
|
1387
|
-
summary: z.string(),
|
|
1388
|
-
wordCount: z.number(),
|
|
1389
|
-
sentiment: z.enum(["positive", "negative", "neutral"]),
|
|
1390
|
-
}),
|
|
1391
|
-
metadata: z.object({
|
|
1392
|
-
timestamp: z.string(),
|
|
1393
|
-
processingTime: z.number(),
|
|
1394
|
-
}),
|
|
1395
|
-
});
|
|
1396
|
-
```
|
|
1397
|
-
|
|
1398
|
-
#### MCP Version Compatibility
|
|
1399
|
-
|
|
1400
|
-
Output schemas are automatically handled based on MCP client version:
|
|
1401
|
-
|
|
1402
|
-
- **MCP v2025-06-18+**: Full output schema support with `structuredContent`
|
|
1403
|
-
- **Earlier versions**: Schemas ignored, standard JSON text response only
|
|
1404
|
-
|
|
1405
|
-
To explicitly set the MCP version for testing:
|
|
1406
|
-
|
|
1407
|
-
```typescript
|
|
1408
|
-
const cli = ArgParser.withMcp({
|
|
1409
|
-
// ... your config
|
|
1410
|
-
}).setMcpProtocolVersion("2025-06-18"); // Enable output schema support
|
|
1411
|
-
```
|
|
1412
|
-
|
|
1413
|
-
**Important**:
|
|
1414
|
-
|
|
1415
|
-
- **CLI users**: You can ignore MCP versions entirely - they don't affect command-line usage
|
|
1416
|
-
- **MCP users**: ArgParser handles version detection automatically based on client capabilities
|
|
1417
|
-
|
|
1418
|
-
#### Automatic Error Handling
|
|
1419
|
-
|
|
1420
|
-
ArgParser automatically handles errors differently based on execution context, so your handlers can simply throw errors without worrying about CLI vs MCP mode:
|
|
1421
|
-
|
|
1422
|
-
```typescript
|
|
1423
|
-
const cli = ArgParser.withMcp({
|
|
1424
|
-
// ... config
|
|
1425
|
-
}).addTool({
|
|
1426
|
-
name: "process-data",
|
|
1427
|
-
handler: async (ctx) => {
|
|
1428
|
-
// Simply throw errors - ArgParser handles the rest automatically
|
|
1429
|
-
if (!ctx.args.apiKey) {
|
|
1430
|
-
throw new Error("API key is required");
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
// Do your work and return results
|
|
1434
|
-
return { success: true, data: processedData };
|
|
1435
|
-
},
|
|
1436
|
-
});
|
|
1437
|
-
```
|
|
1438
|
-
|
|
1439
|
-
**How it works:**
|
|
1440
|
-
|
|
1441
|
-
- **CLI mode**: Thrown errors cause the process to exit with error code 1
|
|
1442
|
-
- **MCP mode**: Thrown errors are automatically converted to structured MCP error responses
|
|
1443
|
-
- **No manual checks needed**: Handlers don't need to check `ctx.isMcp` or handle different response formats
|
|
1444
|
-
|
|
1445
|
-
### Writing Effective MCP Tool Descriptions
|
|
1446
|
-
|
|
1447
|
-
**Why descriptions matter**: When your tools are exposed to Claude Desktop or other MCP clients, the `description` field is the primary way LLMs understand what your tool does and when to use it. A well-written description significantly improves tool selection accuracy and user experience.
|
|
1448
|
-
|
|
1449
|
-
#### Best Practices for Tool Descriptions
|
|
1450
|
-
|
|
1451
|
-
**1. Start with the action** - Begin with a clear verb describing what the tool does:
|
|
1452
|
-
|
|
1453
|
-
```typescript
|
|
1454
|
-
// ✅ Good: Action-first, specific
|
|
1455
|
-
description: "Analyzes text files and returns detailed statistics including word count, character count, and sentiment analysis";
|
|
1456
|
-
|
|
1457
|
-
// ❌ Avoid: Vague or noun-heavy
|
|
1458
|
-
description: "File analysis tool";
|
|
1459
|
-
```
|
|
1460
|
-
|
|
1461
|
-
**2. Include context and use cases** - Explain when and why to use the tool:
|
|
1462
|
-
|
|
1463
|
-
```typescript
|
|
1464
|
-
// ✅ Good: Provides context
|
|
1465
|
-
description: "Converts image files between formats (PNG, JPEG, WebP). Use this when you need to change image format, resize images, or optimize file sizes. Supports batch processing of multiple files.";
|
|
1466
|
-
|
|
1467
|
-
// ❌ Avoid: No context
|
|
1468
|
-
description: "Converts images";
|
|
1469
|
-
```
|
|
1470
|
-
|
|
1471
|
-
**3. Mention key parameters and constraints** - Reference important inputs and limitations:
|
|
1472
|
-
|
|
1473
|
-
```typescript
|
|
1474
|
-
// ✅ Good: Mentions key parameters and constraints
|
|
1475
|
-
description: "Searches through project files using regex patterns. Specify the search pattern and optionally filter by file type. Supports JavaScript, TypeScript, Python, and text files up to 10MB.";
|
|
1476
|
-
|
|
1477
|
-
// ❌ Avoid: No parameter guidance
|
|
1478
|
-
description: "Searches files";
|
|
1479
|
-
```
|
|
1480
|
-
|
|
1481
|
-
**4. Be specific about outputs** - Describe what the tool returns:
|
|
1482
|
-
|
|
1483
|
-
```typescript
|
|
1484
|
-
// ✅ Good: Clear output description
|
|
1485
|
-
description: "Analyzes code complexity and returns metrics including cyclomatic complexity, lines of code, and maintainability index. Results include detailed breakdown by function and overall file scores.";
|
|
1486
|
-
|
|
1487
|
-
// ❌ Avoid: Unclear output
|
|
1488
|
-
description: "Analyzes code";
|
|
1489
|
-
```
|
|
1490
|
-
|
|
1491
|
-
#### Complete Example: Well-Documented Tool
|
|
1492
|
-
|
|
1493
|
-
```typescript
|
|
1494
|
-
.addTool({
|
|
1495
|
-
name: "analyze-repository",
|
|
1496
|
-
description: "Analyzes a Git repository and generates comprehensive statistics including commit history, contributor activity, code quality metrics, and dependency analysis. Use this to understand project health, identify bottlenecks, or prepare reports. Supports Git repositories up to 1GB with history up to 5 years.",
|
|
1497
|
-
flags: [
|
|
1498
|
-
{
|
|
1499
|
-
name: "path",
|
|
1500
|
-
description: "Path to the Git repository root directory",
|
|
1501
|
-
options: ["--path", "-p"],
|
|
1502
|
-
type: "string",
|
|
1503
|
-
mandatory: true,
|
|
1504
|
-
},
|
|
1505
|
-
{
|
|
1506
|
-
name: "include-dependencies",
|
|
1507
|
-
description: "Include analysis of package.json dependencies and security vulnerabilities",
|
|
1508
|
-
options: ["--include-dependencies", "-d"],
|
|
1509
|
-
type: "boolean",
|
|
1510
|
-
flagOnly: true,
|
|
1511
|
-
},
|
|
1512
|
-
{
|
|
1513
|
-
name: "output-format",
|
|
1514
|
-
description: "Output format for the analysis report",
|
|
1515
|
-
options: ["--output-format", "-f"],
|
|
1516
|
-
type: "string",
|
|
1517
|
-
choices: ["json", "markdown", "html"],
|
|
1518
|
-
defaultValue: "json",
|
|
1519
|
-
}
|
|
1520
|
-
],
|
|
1521
|
-
handler: async (ctx) => {
|
|
1522
|
-
// Implementation here
|
|
1523
|
-
}
|
|
1524
|
-
})
|
|
1525
|
-
```
|
|
1526
|
-
|
|
1527
|
-
#### Parameter Description Guidelines
|
|
1528
|
-
|
|
1529
|
-
Each flag should have a clear, concise description:
|
|
1530
|
-
|
|
1531
|
-
```typescript
|
|
1532
|
-
// ✅ Good parameter descriptions
|
|
1533
|
-
{
|
|
1534
|
-
name: "timeout",
|
|
1535
|
-
description: "Maximum execution time in seconds (default: 30, max: 300)",
|
|
1536
|
-
options: ["--timeout", "-t"],
|
|
1537
|
-
type: "number",
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
{
|
|
1541
|
-
name: "verbose",
|
|
1542
|
-
description: "Enable detailed logging output including debug information",
|
|
1543
|
-
options: ["--verbose", "-v"],
|
|
1544
|
-
type: "boolean",
|
|
1545
|
-
flagOnly: true,
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
{
|
|
1549
|
-
name: "format",
|
|
1550
|
-
description: "Output format for results (json: structured data, csv: spreadsheet-friendly, pretty: human-readable)",
|
|
1551
|
-
options: ["--format"],
|
|
1552
|
-
type: "string",
|
|
1553
|
-
choices: ["json", "csv", "pretty"],
|
|
1554
|
-
}
|
|
1555
|
-
```
|
|
1556
|
-
|
|
1557
|
-
#### Common Pitfalls to Avoid
|
|
1558
|
-
|
|
1559
|
-
- **Don't be overly technical**: Avoid jargon that doesn't help with tool selection
|
|
1560
|
-
- **Don't repeat the tool name**: The name is already visible, focus on functionality
|
|
1561
|
-
- **Don't use generic terms**: "Process data" or "handle files" are too vague
|
|
1562
|
-
- **Don't forget constraints**: Mention important limitations or requirements
|
|
1563
|
-
- **Don't ignore parameter descriptions**: Each flag should have a helpful description
|
|
1564
|
-
|
|
1565
|
-
**Remember**: A good description helps the LLM choose the right tool for the task and use it correctly. Invest time in writing clear, comprehensive descriptions - it directly impacts the user experience in Claude Desktop and other MCP clients.
|
|
1566
|
-
|
|
1567
|
-
### Automatic MCP Server Mode (`--s-mcp-serve`)
|
|
1568
|
-
|
|
1569
|
-
You don't need to write any server logic. Run your application with the `--s-mcp-serve` flag, and ArgParser will automatically start a compliant MCP server, exposing all tools defined with `.addTool()` and subcommands created with `.addSubCommand()` (unless `includeSubCommands: false` is set).
|
|
1570
|
-
|
|
1571
|
-
```bash
|
|
1572
|
-
# This single command starts a fully compliant MCP server
|
|
1573
|
-
my-cli-app --s-mcp-serve
|
|
1574
|
-
|
|
1575
|
-
# You can also override transports and ports using system flags
|
|
1576
|
-
my-cli-app --s-mcp-serve --s-mcp-transport sse --s-mcp-port 3001
|
|
1577
|
-
|
|
1578
|
-
# Configure custom log file path for MCP server logs
|
|
1579
|
-
my-cli-app --s-mcp-serve --s-mcp-log-path ./custom-logs/mcp-server.log
|
|
1580
|
-
|
|
1581
|
-
# Or configure logging programmatically in withMcp()
|
|
1582
|
-
const cli = ArgParser.withMcp({
|
|
1583
|
-
appName: 'My CLI App',
|
|
1584
|
-
appCommandName: 'my-cli-app',
|
|
1585
|
-
mcp: {
|
|
1586
|
-
serverInfo: { name: 'my-server', version: '1.0.0' },
|
|
1587
|
-
// NEW: Improved logging with level control
|
|
1588
|
-
log: {
|
|
1589
|
-
level: 'info', // Captures info, warn, error
|
|
1590
|
-
logToFile: './my-logs/mcp-server.log',
|
|
1591
|
-
prefix: 'MyApp'
|
|
1592
|
-
}
|
|
1593
|
-
// LEGACY: logPath: './my-logs/mcp-server.log' // Still works
|
|
1594
|
-
}
|
|
1595
|
-
});
|
|
1596
|
-
```
|
|
1597
|
-
|
|
1598
|
-
### MCP Transports
|
|
1599
|
-
|
|
1600
|
-
You can define the transports directly in the .withMcp() settings, or override them via the `--s-mcp-transport(s)` flags.
|
|
1601
|
-
|
|
1602
|
-
```bash
|
|
1603
|
-
# Single transport
|
|
1604
|
-
my-tool --s-mcp-serve --s-mcp-transport stdio
|
|
1605
|
-
|
|
1606
|
-
# Multiple transports via JSON
|
|
1607
|
-
my-tool --s-mcp-serve --s-mcp-transports '[{"type":"stdio"},{"type":"sse","port":3001}]'
|
|
1608
|
-
|
|
1609
|
-
# Single transport with custom options
|
|
1610
|
-
my-tool --s-mcp-serve --s-mcp-transport sse --s-mcp-port 3000 --s-mcp-host 0.0.0.0
|
|
1611
|
-
|
|
1612
|
-
# Streamable HTTP CORS/auth via CLI flags (JSON strings)
|
|
1613
|
-
my-tool --s-mcp-serve \
|
|
1614
|
-
--s-mcp-transport streamable-http \
|
|
1615
|
-
--s-mcp-port 3002 --s-mcp-path /api/mcp \
|
|
1616
|
-
--s-mcp-cors '{"origins":["http://localhost:5173"],"credentials":true,"methods":["GET","POST","OPTIONS"],"maxAge":600}' \
|
|
1617
|
-
--s-mcp-auth '{"required":true,"scheme":"jwt","jwt":{"algorithms":["HS256"],"secret":"$MY_JWT_SECRET"},"publicPaths":["/health"]}'
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
# Custom log path via CLI flag (logs to specified file instead of ./logs/mcp.log)
|
|
1621
|
-
my-tool --s-mcp-serve --s-mcp-log-path /var/log/my-mcp-server.log
|
|
1622
|
-
|
|
1623
|
-
# Improved logging via programmatic configuration
|
|
1624
|
-
const parser = ArgParser.withMcp({
|
|
1625
|
-
mcp: {
|
|
1626
|
-
serverInfo: { name: 'my-tool', version: '1.0.0' },
|
|
1627
|
-
|
|
1628
|
-
```
|
|
1629
|
-
|
|
1630
|
-
### CORS and Authentication for streamable-http
|
|
1631
|
-
|
|
1632
|
-
CORS is often required when connecting a Web UI to an MCP server over HTTP.
|
|
1633
|
-
|
|
1634
|
-
- Programmatic transport config:
|
|
1635
|
-
|
|
1636
|
-
```ts
|
|
1637
|
-
import type { McpTransportConfig } from "@alcyone-labs/arg-parser";
|
|
1638
|
-
|
|
1639
|
-
const defaultTransports: McpTransportConfig[] = [
|
|
1640
|
-
{
|
|
1641
|
-
type: "streamable-http",
|
|
1642
|
-
port: 3002,
|
|
1643
|
-
path: "/api/mcp",
|
|
1644
|
-
cors: {
|
|
1645
|
-
origins: ["http://localhost:5173", /^https?:\/\/example\.com$/],
|
|
1646
|
-
methods: ["GET", "POST", "OPTIONS"],
|
|
1647
|
-
headers: ["Content-Type", "Authorization", "MCP-Session-Id"],
|
|
1648
|
-
exposedHeaders: ["MCP-Session-Id"],
|
|
1649
|
-
credentials: true,
|
|
1650
|
-
maxAge: 600,
|
|
1651
|
-
},
|
|
1652
|
-
auth: {
|
|
1653
|
-
required: true,
|
|
1654
|
-
scheme: "jwt", // or "bearer"
|
|
1655
|
-
// Bearer allowlist:
|
|
1656
|
-
// allowedTokens: ["token1","token2"],
|
|
1657
|
-
// JWT verification (HS256):
|
|
1658
|
-
// jwt: { algorithms: ["HS256"], secret: process.env.JWT_SECRET },
|
|
1659
|
-
// JWT verification (RS256 with static public key):
|
|
1660
|
-
// jwt: { algorithms: ["RS256"], publicKey: process.env.RS256_PUBLIC_KEY },
|
|
1661
|
-
// JWT verification (RS256 with dynamic JWKS):
|
|
1662
|
-
// jwt: { algorithms: ["RS256"], getPublicKey: async (header)=>{ /* fetch JWKS and return PEM */ } },
|
|
1663
|
-
publicPaths: ["/health"],
|
|
1664
|
-
protectedPaths: undefined, // if set, only listed paths require auth
|
|
1665
|
-
// Optional custom validator to add extra checks
|
|
1666
|
-
validator: async (req, token) => true,
|
|
1667
|
-
},
|
|
1668
|
-
},
|
|
1669
|
-
];
|
|
1670
|
-
```
|
|
1671
|
-
|
|
1672
|
-
- CLI flags (JSON strings):
|
|
1673
|
-
|
|
1674
|
-
```bash
|
|
1675
|
-
my-tool --s-mcp-serve \
|
|
1676
|
-
--s-mcp-transport streamable-http \
|
|
1677
|
-
--s-mcp-port 3002 --s-mcp-path /api/mcp \
|
|
1678
|
-
--s-mcp-cors '{"origins":["http://localhost:5173"],"credentials":true,"methods":["GET","POST","OPTIONS"],"maxAge":600}' \
|
|
1679
|
-
--s-mcp-auth '{"required":true,"scheme":"jwt","jwt":{"algorithms":["HS256"],"secret":"'$JWT_SECRET'"},"publicPaths":["/health"]}'
|
|
1680
|
-
```
|
|
1681
|
-
|
|
1682
|
-
- Express hook for custom routes:
|
|
1683
|
-
|
|
1684
|
-
```ts
|
|
1685
|
-
httpServer: {
|
|
1686
|
-
configureExpress: (app) => {
|
|
1687
|
-
app.get("/health", (_req, res) => res.json({ ok: true }));
|
|
1688
|
-
},
|
|
1689
|
-
}
|
|
1690
|
-
```
|
|
1691
|
-
|
|
1692
|
-
See examples:
|
|
1693
|
-
|
|
1694
|
-
- examples/streamable-http/secure-mcp.ts (HS256)
|
|
1695
|
-
- examples/streamable-http/rs256-mcp.ts (RS256)
|
|
1696
|
-
- examples/streamable-http/jwks-mcp.ts (JWKS)
|
|
1697
|
-
- examples/streamable-http/bearer-mcp.ts (Bearer)
|
|
1698
|
-
- examples/streamable-http/productized-mcp.ts (token + session usage)
|
|
1699
|
-
|
|
1700
|
-
#### TypeScript types
|
|
1701
|
-
|
|
1702
|
-
- CorsOptions
|
|
1703
|
-
|
|
1704
|
-
```ts
|
|
1705
|
-
export type CorsOptions = {
|
|
1706
|
-
origins?: "*" | string | RegExp | Array<string | RegExp>;
|
|
1707
|
-
methods?: string[];
|
|
1708
|
-
headers?: string[];
|
|
1709
|
-
exposedHeaders?: string[];
|
|
1710
|
-
credentials?: boolean;
|
|
1711
|
-
maxAge?: number;
|
|
1712
|
-
};
|
|
1713
|
-
```
|
|
1714
|
-
|
|
1715
|
-
- AuthOptions and JwtVerifyOptions
|
|
1716
|
-
|
|
1717
|
-
```ts
|
|
1718
|
-
export type JwtVerifyOptions = {
|
|
1719
|
-
algorithms?: ("HS256" | "RS256")[];
|
|
1720
|
-
secret?: string; // HS256
|
|
1721
|
-
publicKey?: string; // RS256 static
|
|
1722
|
-
getPublicKey?: (
|
|
1723
|
-
header: Record<string, unknown>,
|
|
1724
|
-
payload: Record<string, unknown>,
|
|
1725
|
-
) => Promise<string> | string; // RS256 dynamic
|
|
1726
|
-
audience?: string | string[];
|
|
1727
|
-
issuer?: string | string[];
|
|
1728
|
-
clockToleranceSec?: number;
|
|
1729
|
-
};
|
|
1730
|
-
|
|
1731
|
-
export type AuthOptions = {
|
|
1732
|
-
required?: boolean; // default true for MCP endpoint
|
|
1733
|
-
scheme?: "bearer" | "jwt";
|
|
1734
|
-
allowedTokens?: string[]; // simple bearer allowlist
|
|
1735
|
-
validator?: (
|
|
1736
|
-
req: any,
|
|
1737
|
-
token: string | undefined,
|
|
1738
|
-
) => boolean | Promise<boolean>;
|
|
1739
|
-
jwt?: JwtVerifyOptions;
|
|
1740
|
-
publicPaths?: string[]; // paths that skip auth
|
|
1741
|
-
protectedPaths?: string[]; // if provided, only these paths require auth
|
|
1742
|
-
customMiddleware?: (req: any, res: any, next: any) => any; // full control hook
|
|
1743
|
-
};
|
|
1744
|
-
```
|
|
1745
|
-
|
|
1746
|
-
- HttpServerOptions
|
|
1747
|
-
|
|
1748
|
-
```ts
|
|
1749
|
-
export type HttpServerOptions = {
|
|
1750
|
-
configureExpress?: (app: any) => void;
|
|
1751
|
-
};
|
|
1752
|
-
```
|
|
1753
|
-
|
|
1754
|
-
Notes:
|
|
1755
|
-
|
|
1756
|
-
- When credentials are true, Access-Control-Allow-Origin echoes the request Origin rather than using "\*".
|
|
1757
|
-
- You can manage CORS for non-MCP routes in configureExpress.
|
|
1758
|
-
- Use publicPaths to allow some routes without auth; use protectedPaths to only require auth for certain routes.
|
|
1759
|
-
|
|
1760
|
-
log: {
|
|
1761
|
-
level: 'debug', // Capture all log levels
|
|
1762
|
-
logToFile: '/var/log/my-mcp-server.log',
|
|
1763
|
-
prefix: 'MyTool'
|
|
1764
|
-
}
|
|
1765
|
-
// LEGACY: logPath: '/var/log/my-mcp-server.log' // Still works
|
|
1766
|
-
}
|
|
1767
|
-
|
|
1768
|
-
### Adding custom HTTP routes (e.g., /health)
|
|
1769
|
-
|
|
1770
|
-
Use the httpServer.configureExpress(app) hook to register routes before MCP endpoints are bound. Example:
|
|
1771
|
-
|
|
1772
|
-
```ts
|
|
1773
|
-
const cli = ArgParser.withMcp({
|
|
1774
|
-
mcp: {
|
|
1775
|
-
serverInfo: { name: "my-mcp", version: "1.0.0" },
|
|
1776
|
-
defaultTransports: [
|
|
1777
|
-
{
|
|
1778
|
-
type: "streamable-http",
|
|
1779
|
-
port: 3002,
|
|
1780
|
-
path: "/api/mcp",
|
|
1781
|
-
auth: { required: true, publicPaths: ["/health"] },
|
|
1782
|
-
},
|
|
1783
|
-
],
|
|
1784
|
-
httpServer: {
|
|
1785
|
-
configureExpress: (app) =>
|
|
1786
|
-
app.get("/health", (_req, res) => res.json({ ok: true })),
|
|
1787
|
-
},
|
|
1788
|
-
},
|
|
1789
|
-
});
|
|
1790
|
-
```
|
|
1791
|
-
|
|
1792
|
-
- To make a route public (no auth), add it to auth.publicPaths.
|
|
1793
|
-
- CORS headers for non-MCP paths can be applied by your own middleware inside the hook if desired.
|
|
1794
|
-
|
|
1795
|
-
### Multiple transports and improved logging
|
|
1796
|
-
|
|
1797
|
-
const cli = ArgParser.withMcp({
|
|
1798
|
-
appName: 'multi-tool',
|
|
1799
|
-
appCommandName: 'multi-tool',
|
|
1800
|
-
mcp: {
|
|
1801
|
-
// NEW: improved logging configuration
|
|
1802
|
-
log: {
|
|
1803
|
-
level: 'info',
|
|
1804
|
-
logToFile: './logs/multi-tool-mcp.log',
|
|
1805
|
-
prefix: 'MultiTool'
|
|
1806
|
-
},
|
|
1807
|
-
serverInfo: {
|
|
1808
|
-
name: 'multi-tool-mcp',
|
|
1809
|
-
version: '1.0.0'
|
|
1810
|
-
},
|
|
1811
|
-
transports: [
|
|
1812
|
-
// Can be a single string...
|
|
1813
|
-
"stdio",
|
|
1814
|
-
// or one of the other transport types supported by @modelcontextprotocol/sdk
|
|
1815
|
-
{ type: "sse", port: 3000, host: "0.0.0.0" },
|
|
1816
|
-
{ type: "websocket", port: 3001, path: "/ws" }
|
|
1817
|
-
]
|
|
1818
|
-
}
|
|
1819
|
-
});
|
|
1820
|
-
|
|
1821
|
-
````
|
|
1822
|
-
|
|
1823
|
-
### MCP Logging Configuration
|
|
1824
|
-
|
|
1825
|
-
MCP server logging can be configured with McpLoggerOptions options using `@alcyone-labs/simple-mcp-logger`. You can control log levels, output destinations, and more.
|
|
1826
|
-
|
|
1827
|
-
#### Enhanced Logging (Recommended)
|
|
1828
|
-
|
|
1829
|
-
Use the new `log` property for comprehensive logging control:
|
|
1830
|
-
|
|
1831
|
-
```typescript
|
|
1832
|
-
const parser = ArgParser.withMcp({
|
|
1833
|
-
appName: "My MCP Server",
|
|
1834
|
-
appCommandName: "my-mcp-server",
|
|
1835
|
-
mcp: {
|
|
1836
|
-
serverInfo: { name: "my-server", version: "1.0.0" },
|
|
1837
|
-
log: {
|
|
1838
|
-
level: "debug", // Captures debug, info, warn, error
|
|
1839
|
-
logToFile: "./logs/comprehensive.log",
|
|
1840
|
-
prefix: "MyServer",
|
|
1841
|
-
mcpMode: true, // MCP compliant (default)
|
|
1842
|
-
},
|
|
1843
|
-
},
|
|
1844
|
-
});
|
|
1845
|
-
````
|
|
1846
|
-
|
|
1847
|
-
**Available log levels**: `"debug"` | `"info"` | `"warn"` | `"error"` | `"silent"`
|
|
1848
|
-
|
|
1849
|
-
**Type Safety**: The `McpLoggerOptions` type is provided for full TypeScript support and matches the interface from `@alcyone-labs/simple-mcp-logger`.
|
|
1850
|
-
|
|
1851
|
-
#### Simple Logging Configuration
|
|
1852
|
-
|
|
1853
|
-
For basic use cases, you can use a simple string path:
|
|
1854
|
-
|
|
1855
|
-
```typescript
|
|
1856
|
-
const parser = ArgParser.withMcp({
|
|
1857
|
-
mcp: {
|
|
1858
|
-
serverInfo: { name: "my-server", version: "1.0.0" },
|
|
1859
|
-
log: "./logs/simple.log", // Simple string path
|
|
1860
|
-
},
|
|
1861
|
-
});
|
|
1862
|
-
```
|
|
1863
|
-
|
|
1864
|
-
#### Configuration Priority
|
|
1865
|
-
|
|
1866
|
-
Logging configuration follows this priority order:
|
|
1867
|
-
|
|
1868
|
-
1. **CLI Flag (Highest Priority)**: `--s-mcp-log-path <path>`
|
|
1869
|
-
2. **Merging**: When both `mcp.log` and `mcp.logPath` are present:
|
|
1870
|
-
- `mcp.log` provides logger options (level, prefix, mcpMode)
|
|
1871
|
-
- `mcp.logPath` provides flexible path resolution (relativeTo, basePath)
|
|
1872
|
-
- Path resolution: `mcp.logPath` > `mcp.log.logToFile`
|
|
1873
|
-
3. **Log Config Only**: `mcp.log` object or string in `withMcp()`
|
|
1874
|
-
4. **Legacy Log Path Only**: `mcp.logPath` in `withMcp()`
|
|
1875
|
-
5. **Default Path (Fallback)**: `./logs/mcp.log`
|
|
1876
|
-
|
|
1877
|
-
#### Configuration Merging
|
|
1878
|
-
|
|
1879
|
-
When both `log` and `logPath` are specified:
|
|
1880
|
-
|
|
1881
|
-
```typescript
|
|
1882
|
-
const parser = ArgParser.withMcp({
|
|
1883
|
-
mcp: {
|
|
1884
|
-
serverInfo: { name: "my-server", version: "1.0.0" },
|
|
1885
|
-
// log provides logger options (level, prefix, mcpMode)
|
|
1886
|
-
log: {
|
|
1887
|
-
level: "debug",
|
|
1888
|
-
prefix: "MyServer",
|
|
1889
|
-
mcpMode: true,
|
|
1890
|
-
// logToFile can be omitted when using logPath
|
|
1891
|
-
},
|
|
1892
|
-
// logPath provides flexible path resolution
|
|
1893
|
-
logPath: {
|
|
1894
|
-
path: "./logs/app.log",
|
|
1895
|
-
relativeTo: "entry", // "entry" | "cwd" | "absolute"
|
|
1896
|
-
basePath: "/custom/base", // Optional custom base path
|
|
1897
|
-
},
|
|
1898
|
-
},
|
|
1899
|
-
});
|
|
1900
|
-
```
|
|
1901
|
-
|
|
1902
|
-
**Merging behavior:**
|
|
1903
|
-
|
|
1904
|
-
- `log` provides logger configuration (level, prefix, mcpMode)
|
|
1905
|
-
- `logPath` provides flexible path resolution with `relativeTo` options
|
|
1906
|
-
- If both specify a file path, `logPath` takes precedence for path resolution
|
|
1907
|
-
- This preserves the powerful `LogPath` features while using `McpLoggerOptions` for logger settings
|
|
1908
|
-
|
|
1909
|
-
#### Path Resolution Options
|
|
1910
|
-
|
|
1911
|
-
Log paths are resolved with smart defaults for better DXT package compatibility:
|
|
1912
|
-
|
|
1913
|
-
```typescript
|
|
1914
|
-
// Simple string paths (recommended)
|
|
1915
|
-
const parser = ArgParser.withMcp({
|
|
1916
|
-
appName: "My CLI",
|
|
1917
|
-
appCommandName: "my-cli",
|
|
1918
|
-
mcp: {
|
|
1919
|
-
serverInfo: { name: "my-server", version: "1.0.0" },
|
|
1920
|
-
logPath: "./logs/app.log", // Relative to entry point (default)
|
|
1921
|
-
// logPath: "/tmp/app.log", // Absolute paths work too
|
|
1922
|
-
// logPath: "cwd:./logs/app.log", // Explicit process.cwd() relative
|
|
1923
|
-
},
|
|
1924
|
-
});
|
|
1925
|
-
|
|
1926
|
-
// Object configuration for more granular use cases
|
|
1927
|
-
const parser = ArgParser.withMcp({
|
|
1928
|
-
// ... other config
|
|
1929
|
-
mcp: {
|
|
1930
|
-
// ... server info
|
|
1931
|
-
logPath: {
|
|
1932
|
-
path: "./logs/app.log",
|
|
1933
|
-
relativeTo: "entry", // "entry" | "cwd" | "absolute"
|
|
1934
|
-
basePath: "/custom/base", // Optional custom base path
|
|
1935
|
-
},
|
|
1936
|
-
},
|
|
1937
|
-
});
|
|
1938
|
-
|
|
1939
|
-
// CLI flag overrides programmatic setting
|
|
1940
|
-
// my-cli --s-mcp-serve --s-mcp-log-path ./override.log
|
|
1941
|
-
```
|
|
1942
|
-
|
|
1943
|
-
The CLI flag always takes precedence, allowing users to override the developer's programmatic configuration when needed. By default, relative paths resolve relative to the application's entry point, making logs predictably located near DXT packages.
|
|
1944
|
-
|
|
1945
|
-
### MCP Lifecycle Events
|
|
1946
|
-
|
|
1947
|
-
ArgParser MCP servers support lifecycle events that allow you to perform initialization, cleanup, and other operations at specific points in the MCP protocol flow. These events are particularly useful for database connections, resource setup, and graceful shutdown procedures.
|
|
1948
|
-
|
|
1949
|
-
```typescript
|
|
1950
|
-
const cli = ArgParser.withMcp({
|
|
1951
|
-
appName: "Database CLI",
|
|
1952
|
-
appCommandName: "db-cli",
|
|
1953
|
-
mcp: {
|
|
1954
|
-
serverInfo: { name: "database-server", version: "1.0.0" },
|
|
1955
|
-
lifecycle: {
|
|
1956
|
-
onInitialize: async (ctx) => {
|
|
1957
|
-
// Called when client sends "initialize" request
|
|
1958
|
-
// Perfect for database connections, resource setup
|
|
1959
|
-
ctx.logger.mcpError("Initializing server...");
|
|
1960
|
-
|
|
1961
|
-
const dbUrl = ctx.getFlag("database_url");
|
|
1962
|
-
if (dbUrl) {
|
|
1963
|
-
await connectToDatabase(dbUrl);
|
|
1964
|
-
ctx.logger.mcpError("Database connected successfully");
|
|
1965
|
-
}
|
|
1966
|
-
},
|
|
1967
|
-
|
|
1968
|
-
onInitialized: async (ctx) => {
|
|
1969
|
-
// Called when client sends "initialized" notification
|
|
1970
|
-
// Server is ready for normal operations
|
|
1971
|
-
ctx.logger.mcpError("Server ready for requests");
|
|
1972
|
-
await startBackgroundTasks();
|
|
1973
|
-
},
|
|
1974
|
-
|
|
1975
|
-
onShutdown: async (ctx) => {
|
|
1976
|
-
// Called during server shutdown
|
|
1977
|
-
// Perfect for cleanup, closing connections
|
|
1978
|
-
ctx.logger.mcpError(`Shutting down: ${ctx.reason}`);
|
|
1979
|
-
await cleanupResources();
|
|
1980
|
-
await closeDatabase();
|
|
1981
|
-
},
|
|
1982
|
-
},
|
|
1983
|
-
},
|
|
1984
|
-
});
|
|
1985
|
-
```
|
|
1986
|
-
|
|
1987
|
-
**Lifecycle Events:**
|
|
1988
|
-
|
|
1989
|
-
- **`onInitialize`**: Called when a client sends an "initialize" request. Ideal for database connections, resource initialization, configuration validation, and authentication setup.
|
|
1990
|
-
- **`onInitialized`**: Called when a client sends an "initialized" notification, indicating the client is ready for normal operations. Perfect for final setup steps and background task initialization.
|
|
1991
|
-
- **`onShutdown`**: Called when the MCP server is shutting down. Essential for cleanup, resource disposal, and graceful shutdown procedures.
|
|
1992
|
-
|
|
1993
|
-
**Context Properties:**
|
|
1994
|
-
|
|
1995
|
-
Each lifecycle event receives a context object with:
|
|
1996
|
-
|
|
1997
|
-
- `getFlag(name)`: Access parsed CLI flags and environment variables
|
|
1998
|
-
- `logger`: MCP-compliant logger instance for the current context
|
|
1999
|
-
- `serverInfo`: Server information (name, version, description)
|
|
2000
|
-
- `clientInfo`: Client information (available in onInitialize and onInitialized)
|
|
2001
|
-
- `protocolVersion`: MCP protocol version being used
|
|
2002
|
-
- `reason`: Shutdown reason (only in onShutdown: "client_disconnect", "server_shutdown", "error", "signal")
|
|
2003
|
-
|
|
2004
|
-
### MCP Resources - Real-Time Data Feeds
|
|
2005
|
-
|
|
2006
|
-
MCP Resources enable your CLI tools to provide **real-time, subscription-based data feeds** to AI assistants. Unlike tools (which are called once), resources can be subscribed to and provide live updates when data changes.
|
|
2007
|
-
|
|
2008
|
-
**Key Benefits:**
|
|
2009
|
-
|
|
2010
|
-
- **Real-time notifications**: AI assistants get notified when your data changes
|
|
2011
|
-
- **Flexible URI templates**: Support dynamic parameters like `data://alerts/aged/gte:{threshold}`
|
|
2012
|
-
- **Standard MCP pattern**: Full subscription lifecycle support
|
|
2013
|
-
- **Zero CLI impact**: Resources only work in MCP mode, CLI usage unchanged
|
|
2014
|
-
|
|
2015
|
-
#### Basic Resource Setup
|
|
2016
|
-
|
|
2017
|
-
```typescript
|
|
2018
|
-
const parser = ArgParser.withMcp({
|
|
2019
|
-
appName: "Data Monitor",
|
|
2020
|
-
appCommandName: "data-monitor",
|
|
2021
|
-
mcp: {
|
|
2022
|
-
serverInfo: { name: "data-monitor", version: "1.0.0" },
|
|
2023
|
-
},
|
|
2024
|
-
}).addMcpResource({
|
|
2025
|
-
name: "recent-data",
|
|
2026
|
-
uriTemplate: "data://recent",
|
|
2027
|
-
title: "Recent Data",
|
|
2028
|
-
description: "Get the most recent data entries",
|
|
2029
|
-
mimeType: "application/json",
|
|
2030
|
-
handler: async (uri) => {
|
|
2031
|
-
const recentData = await getRecentData();
|
|
2032
|
-
return {
|
|
2033
|
-
contents: [
|
|
2034
|
-
{
|
|
2035
|
-
uri: uri.href,
|
|
2036
|
-
text: JSON.stringify(recentData, null, 2),
|
|
2037
|
-
mimeType: "application/json",
|
|
2038
|
-
},
|
|
2039
|
-
],
|
|
2040
|
-
};
|
|
2041
|
-
},
|
|
2042
|
-
});
|
|
2043
|
-
```
|
|
2044
|
-
|
|
2045
|
-
#### URI Templates with Dynamic Parameters
|
|
2046
|
-
|
|
2047
|
-
Create flexible resources that accept parameters:
|
|
2048
|
-
|
|
2049
|
-
```typescript
|
|
2050
|
-
.addMcpResource({
|
|
2051
|
-
name: "aged-data-alert",
|
|
2052
|
-
uriTemplate: "data://alerts/aged/gte:{threshold}",
|
|
2053
|
-
title: "Aged Data Alert",
|
|
2054
|
-
description: "Monitor data that has aged past a threshold (in milliseconds)",
|
|
2055
|
-
handler: async (uri, { threshold }) => {
|
|
2056
|
-
const thresholdMs = parseInt(threshold);
|
|
2057
|
-
const agedData = await getDataOlderThan(new Date(Date.now() - thresholdMs));
|
|
2058
|
-
|
|
2059
|
-
return {
|
|
2060
|
-
contents: [{
|
|
2061
|
-
uri: uri.href,
|
|
2062
|
-
text: JSON.stringify({
|
|
2063
|
-
threshold_ms: thresholdMs,
|
|
2064
|
-
query_time: new Date().toISOString(),
|
|
2065
|
-
aged_data: agedData,
|
|
2066
|
-
count: agedData.length
|
|
2067
|
-
}, null, 2),
|
|
2068
|
-
mimeType: "application/json"
|
|
2069
|
-
}]
|
|
2070
|
-
};
|
|
2071
|
-
}
|
|
2072
|
-
});
|
|
2073
|
-
```
|
|
2074
|
-
|
|
2075
|
-
#### MCP Subscription Lifecycle
|
|
2076
|
-
|
|
2077
|
-
Resources support the full MCP subscription pattern:
|
|
2078
|
-
|
|
2079
|
-
1. **Client subscribes**: `resources/subscribe` → `"data://alerts/aged/gte:10000"`
|
|
2080
|
-
2. **Server monitors**: Your application detects data changes
|
|
2081
|
-
3. **Server notifies**: `notifications/resources/updated` sent to subscribed clients
|
|
2082
|
-
4. **Client reads fresh data**: `resources/read` → `"data://alerts/aged/gte:10000"`
|
|
2083
|
-
5. **Client unsubscribes**: `resources/unsubscribe` when done
|
|
2084
|
-
|
|
2085
|
-
#### Usage Examples
|
|
2086
|
-
|
|
2087
|
-
**AI Assistant Integration:**
|
|
2088
|
-
|
|
2089
|
-
```typescript
|
|
2090
|
-
// AI assistant can subscribe to real-time data
|
|
2091
|
-
await client.request("resources/subscribe", {
|
|
2092
|
-
uri: "data://alerts/aged/gte:60000", // 1 minute threshold
|
|
2093
|
-
});
|
|
2094
|
-
|
|
2095
|
-
// Handle notifications
|
|
2096
|
-
client.on("notifications/resources/updated", async (notification) => {
|
|
2097
|
-
const response = await client.request("resources/read", {
|
|
2098
|
-
uri: notification.uri,
|
|
2099
|
-
});
|
|
2100
|
-
console.log("Fresh data:", JSON.parse(response.contents[0].text));
|
|
2101
|
-
});
|
|
2102
|
-
```
|
|
2103
|
-
|
|
2104
|
-
**Command Line Testing:**
|
|
2105
|
-
|
|
2106
|
-
```bash
|
|
2107
|
-
# Start MCP server
|
|
2108
|
-
data-monitor --s-mcp-serve
|
|
2109
|
-
|
|
2110
|
-
# Test resource (in another terminal)
|
|
2111
|
-
echo '{"jsonrpc":"2.0","id":1,"method":"resources/read","params":{"uri":"data://alerts/aged/gte:10000"}}' | data-monitor --s-mcp-serve
|
|
2112
|
-
```
|
|
2113
|
-
|
|
2114
|
-
#### Design Patterns
|
|
2115
|
-
|
|
2116
|
-
**Static Resources**: Use simple URIs for data that changes content but not structure
|
|
2117
|
-
|
|
2118
|
-
```typescript
|
|
2119
|
-
uriTemplate: "logs://recent"; // Always available, content updates
|
|
2120
|
-
uriTemplate: "status://system"; // System status, updates in real-time
|
|
2121
|
-
```
|
|
2122
|
-
|
|
2123
|
-
**Parameterized Resources**: Use URI templates for flexible filtering
|
|
2124
|
-
|
|
2125
|
-
```typescript
|
|
2126
|
-
uriTemplate: "data://type/{type}"; // Filter by type
|
|
2127
|
-
uriTemplate: "alerts/{severity}/gte:{age}"; // Multiple parameters
|
|
2128
|
-
uriTemplate: "search/{query}/limit:{count}"; // Search with limits
|
|
2129
|
-
```
|
|
2130
|
-
|
|
2131
|
-
**Time-Based Resources**: Perfect for monitoring and alerting
|
|
2132
|
-
|
|
2133
|
-
```typescript
|
|
2134
|
-
uriTemplate: "events/since:{timestamp}"; // Events since timestamp
|
|
2135
|
-
uriTemplate: "metrics/aged/gte:{threshold}"; // Metrics past threshold
|
|
2136
|
-
uriTemplate: "logs/errors/last:{duration}"; // Recent errors
|
|
2137
|
-
```
|
|
2138
|
-
|
|
2139
|
-
> **💡 Pro Tip**: Resources are perfect for monitoring, alerting, and real-time data feeds. They complement tools (one-time actions) by providing continuous data streams that AI assistants can subscribe to.
|
|
2140
|
-
|
|
2141
|
-
### Automatic Console Safety
|
|
2142
|
-
|
|
2143
|
-
A major challenge in MCP is preventing `console.log` from corrupting the JSON-RPC communication over `STDOUT`. ArgParser solves this automatically.
|
|
2144
|
-
|
|
2145
|
-
- **How it works**: When `--s-mcp-serve` is active, ArgParser hijacks the global `console` object.
|
|
2146
|
-
- **What it does**: It redirects `console.log`, `.info`, `.warn`, and `.debug` to `STDERR` with a prefix, making them visible for debugging without interfering with the protocol. `console.error` is preserved on `STDERR` as expected.
|
|
2147
|
-
- **Your benefit**: You can write `console.log` statements freely in your handlers. They will work as expected in CLI mode and be safely handled in MCP mode with **zero code changes**.
|
|
2148
|
-
|
|
2149
|
-
### Generating DXT Packages (`--s-build-dxt`)
|
|
2150
|
-
|
|
2151
|
-
A Desktop Extension (`.dxt`) is a standardized package for installing your tools into Claude Desktop. ArgParser automates this process.
|
|
2152
|
-
|
|
2153
|
-
```bash
|
|
2154
|
-
# 1. Generate the DXT package contents into a directory
|
|
2155
|
-
my-cli-app --s-build-dxt ./my-dxt-package
|
|
2156
|
-
|
|
2157
|
-
# The output folder contains everything needed: manifest.json, entry point, etc.
|
|
2158
|
-
# A default logo will be applied if you don't provide one.
|
|
2159
|
-
|
|
2160
|
-
# 2. (Optional) Pack the folder into a .dxt file for distribution
|
|
2161
|
-
# (you can install the unpacked folder) directly in Claude Desktop > Settings > Extensions > Advanced
|
|
2162
|
-
npx @anthropic-ai/dxt pack ./my-dxt-package
|
|
2163
|
-
|
|
2164
|
-
# 3. (Optional) Sign the DXT package - this has not been well tested yet
|
|
2165
|
-
npx @anthropic-ai/dxt sign ./my-dxt-package.dxt
|
|
2166
|
-
|
|
2167
|
-
# Then drag & drop the .dxt file into Claude Desktop to install it, in the Settings > Extensions screen.
|
|
2168
|
-
|
|
2169
|
-
# **IMPORTANT**:
|
|
2170
|
-
# If you use ML models or packages that include binaries such as Sqlite3 or sharp, etc...
|
|
2171
|
-
# You need to bundle the node_modules folder with your DXT package
|
|
2172
|
-
# In order to do this, you need to use the following flag:
|
|
2173
|
-
# First hard-install all the packages
|
|
2174
|
-
rm -rf node_moduels
|
|
2175
|
-
pnpm install --prod --linker hoisted
|
|
2176
|
-
# Then bundle with node_modules
|
|
2177
|
-
mycli --s-build-dxt ./my-dxt-package --s-with-node-modules
|
|
2178
|
-
# then build the dxt bundle
|
|
2179
|
-
npx @anthropic-ai/dxt pack ./my-dxt-package
|
|
2180
|
-
# then upload the dxt bundle to Claude Desktop from the settings > extensions > advanced
|
|
2181
|
-
```
|
|
2182
|
-
|
|
2183
|
-
### Logo Configuration
|
|
2184
|
-
|
|
2185
|
-
The logo will appear in Claude Desktop's Extensions settings and when users interact with your MCP tools. Note that neither ArgParser nor Anthropic packer will modify the logo, so make sure to use a reasonable size, such as 256x256 pixels or 512x512 pixels maximum. Any image type that can display in a browser is supported.
|
|
2186
|
-
|
|
2187
|
-
You can customize the logo/icon that appears in Claude Desktop for your DXT package by configuring the `logo` property in your `serverInfo`:
|
|
2188
|
-
|
|
2189
|
-
```typescript
|
|
2190
|
-
const cli = ArgParser.withMcp({
|
|
2191
|
-
appName: "My CLI",
|
|
2192
|
-
appCommandName: "mycli",
|
|
2193
|
-
mcp: {
|
|
2194
|
-
// This will appear in Claude Desktop's Extensions settings
|
|
2195
|
-
serverInfo: {
|
|
2196
|
-
name: "my-mcp-server",
|
|
2197
|
-
version: "1.0.0",
|
|
2198
|
-
description: "My CLI as an MCP server",
|
|
2199
|
-
logo: "./assets/my-logo.png", // Local file path
|
|
2200
|
-
},
|
|
2201
|
-
},
|
|
2202
|
-
});
|
|
2203
|
-
```
|
|
2204
|
-
|
|
2205
|
-
If no custom logo is provided or loading fails, a default ArgParser logo is included
|
|
2206
|
-
|
|
2207
|
-
#### Supported Logo Sources
|
|
2208
|
-
|
|
2209
|
-
**Local File Path:**
|
|
2210
|
-
|
|
2211
|
-
```typescript
|
|
2212
|
-
logo: "./assets/my-logo.png"; // Relative to your project
|
|
2213
|
-
logo: "/absolute/path/to/logo.jpg"; // Absolute path
|
|
2214
|
-
```
|
|
2215
|
-
|
|
2216
|
-
**HTTP/HTTPS URL:**
|
|
2217
|
-
|
|
2218
|
-
```typescript
|
|
2219
|
-
logo: "https://example.com/logo.png"; // Downloaded automatically
|
|
2220
|
-
logo: "https://cdn.example.com/icon.svg";
|
|
2221
|
-
```
|
|
2222
|
-
|
|
2223
|
-
### Including Additional Files in DXT Packages
|
|
2224
|
-
|
|
2225
|
-
You can include additional files and directories in your DXT package using the `dxt.include` configuration. This is useful for bundling database migrations, configuration files, assets, or any other files your MCP server needs at runtime.
|
|
2226
|
-
|
|
2227
|
-
```typescript
|
|
2228
|
-
const cli = ArgParser.withMcp({
|
|
2229
|
-
appName: "My CLI",
|
|
2230
|
-
appCommandName: "mycli",
|
|
2231
|
-
mcp: {
|
|
2232
|
-
serverInfo: {
|
|
2233
|
-
name: "my-mcp-server",
|
|
2234
|
-
version: "1.0.0",
|
|
2235
|
-
description: "My CLI as an MCP server",
|
|
2236
|
-
},
|
|
2237
|
-
dxt: {
|
|
2238
|
-
include: [
|
|
2239
|
-
"migrations", // Copy entire migrations folder
|
|
2240
|
-
"config/production.json", // Copy specific file
|
|
2241
|
-
{ from: "assets/logo.png", to: "logo.png" }, // Copy and rename file
|
|
2242
|
-
{ from: "scripts", to: "bin" }, // Copy folder with new name
|
|
2243
|
-
],
|
|
2244
|
-
},
|
|
2245
|
-
},
|
|
2246
|
-
});
|
|
2247
|
-
```
|
|
2248
|
-
|
|
2249
|
-
#### Include Options
|
|
2250
|
-
|
|
2251
|
-
**Simple string paths** - Copy files/directories to the same relative location:
|
|
2252
|
-
|
|
2253
|
-
```typescript
|
|
2254
|
-
include: [
|
|
2255
|
-
"migrations", // Copies ./migrations/ to dxt/migrations/
|
|
2256
|
-
"config/default.json", // Copies ./config/default.json to dxt/config/default.json
|
|
2257
|
-
];
|
|
2258
|
-
```
|
|
2259
|
-
|
|
2260
|
-
**Object mapping** - Copy with custom destination paths:
|
|
2261
|
-
|
|
2262
|
-
```typescript
|
|
2263
|
-
include: [
|
|
2264
|
-
{ from: "config/prod.json", to: "config.json" }, // Rename during copy
|
|
2265
|
-
{ from: "database/schema", to: "db/schema" }, // Copy to different path
|
|
2266
|
-
];
|
|
2267
|
-
```
|
|
2268
|
-
|
|
2269
|
-
**Path Resolution**: All paths in the `from` field are resolved relative to your project root (where `package.json` and `tsconfig.json` are located).
|
|
2270
|
-
|
|
2271
|
-
**Example Use Cases**:
|
|
2272
|
-
|
|
2273
|
-
- Database migration files for initialization
|
|
2274
|
-
- Configuration templates or defaults
|
|
2275
|
-
- Static assets like images or documents
|
|
2276
|
-
- Scripts or utilities needed at runtime
|
|
2277
|
-
- Documentation or help files
|
|
2278
|
-
|
|
2279
|
-
### How DXT Generation Works
|
|
2280
|
-
|
|
2281
|
-
When you run `--s-build-dxt`, ArgParser performs several steps to create a self-contained, autonomous package:
|
|
2282
|
-
|
|
2283
|
-
1. **Introspection**: It analyzes all tools defined with `.addTool()`.
|
|
2284
|
-
2. **Manifest Generation**: It creates a `manifest.json` file.
|
|
2285
|
-
- Tool flags are converted into a JSON Schema for the `input_schema`.
|
|
2286
|
-
- Flags with an `env` property (e.g., `{ name: 'apiKey', env: 'API_KEY' }`) provide universal `process.env` fallback/sync and are automatically added to the `user_config` section of the DXT manifest.
|
|
2287
|
-
3. **Autonomous Build**: It bundles your CLI's source code and its dependencies into a single entry point (e.g., `server.js`) that can run without `node_modules`. This ensures the DXT is portable and reliable. If you have properly setup your node_modules (via `pnpm install --prod --node-linker=hoisted`) and pass `--s-with-node-nodules` to the bundling process, the resulting DXT will include all necessary dependencies, this is useful for projects that require native dependencies or have complex dependency trees.
|
|
2288
|
-
4. **Packaging**: It assembles all necessary files (manifest, server bundle, logo, etc.) into the specified output directory, ready to be used by Claude Desktop or packed with `npx @anthropic-ai/dxt`.
|
|
2289
|
-
|
|
2290
|
-
### DXT Bundling Strategies
|
|
2291
|
-
|
|
2292
|
-
ArgParser offers two approaches for handling dependencies in DXT packages, depending on your project's needs.
|
|
2293
|
-
|
|
2294
|
-
#### Standard Approach (Recommended for Most Projects)
|
|
2295
|
-
|
|
2296
|
-
```bash
|
|
2297
|
-
# For pure JavaScript/TypeScript projects
|
|
2298
|
-
your-cli --s-build-dxt
|
|
2299
|
-
```
|
|
2300
|
-
|
|
2301
|
-
- **Best for**: Pure JS/TS projects without native dependencies
|
|
2302
|
-
- **Bundle size**: Small (5-10MB typical)
|
|
2303
|
-
- **Build time**: Fast
|
|
2304
|
-
- **Dependencies**: Bundled automatically by TSDown
|
|
2305
|
-
|
|
2306
|
-
#### Native Dependencies Approach
|
|
2307
|
-
|
|
2308
|
-
```bash
|
|
2309
|
-
# For projects with native binaries (ONNX, Sharp, SQLite, etc.)
|
|
2310
|
-
rm -rf node_modules
|
|
2311
|
-
pnpm install --prod --node-linker=hoisted
|
|
2312
|
-
your-cli --s-build-dxt --s-with-node-modules
|
|
2313
|
-
```
|
|
2314
|
-
|
|
2315
|
-
- **Best for**: Projects using ONNX Runtime, Sharp, Canvas, SQLite, or other packages with `.node` binaries
|
|
2316
|
-
- **Bundle size**: Larger (50-200MB typical)
|
|
2317
|
-
- **Build time**: Longer (copies entire node_modules)
|
|
2318
|
-
- **Dependencies**: Complete autonomy - no installation needed by Claude
|
|
2319
|
-
|
|
2320
|
-
**When to use `--s-with-node-modules`:**
|
|
2321
|
-
|
|
2322
|
-
- ✅ Your project uses machine learning packages (ONNX Runtime, TensorFlow bindings)
|
|
2323
|
-
- ✅ You need image processing (Sharp, Canvas)
|
|
2324
|
-
- ✅ You use database packages with native binaries (better-sqlite3, sqlite3)
|
|
2325
|
-
- ✅ You want guaranteed compatibility without runtime installation
|
|
2326
|
-
- ✅ Bundle size is acceptable for your use case
|
|
2327
|
-
|
|
2328
|
-
**Required preparation steps:**
|
|
2329
|
-
|
|
2330
|
-
1. `rm -rf node_modules` - Clean slate for proper structure
|
|
2331
|
-
2. `pnpm install --prod --node-linker=hoisted` - Creates flat, symlink-free structure
|
|
2332
|
-
3. Add `--s-with-node-modules` flag to your build command
|
|
2333
|
-
|
|
2334
|
-
The system automatically validates your setup and provides guidance if issues are detected.
|
|
2335
|
-
|
|
2336
|
-
### Typical Errors
|
|
2337
|
-
|
|
2338
|
-
**Failed to run in Claude Desktop**:
|
|
2339
|
-
|
|
2340
|
-
Claude Desktop is pretty finicky (as of Claude 0.12.28), and the built-in Node.js does not work with extensions built with `--s-with-node-modules` and installed via ArgParser (and I have no idea why because there's no debug info).
|
|
2341
|
-
To resolve this, simply go to `Claude Desktop > Settings > Extensions > Advanced Settings` and turn **OFF** `Use Built-in Node.js for MCP`.
|
|
2342
|
-
|
|
2343
|
-
Note that there are _many_ reasons for extensions not to work, if it does not work with Built-in or System Node.js, then something in your app is wrong. Feel free to join Alcyone Labs' discord for support: [Alcyone Labs' Discord](https://discord.gg/rRHhpz5nS5)
|
|
2344
|
-
|
|
2345
|
-
**Failed to attach to MCP when downloading external assets**
|
|
2346
|
-
|
|
2347
|
-
Sometimes, the MCP client needs to install external files, for example an ML model from HuggingFace or some task that takes more than 10 seconds to run. While it's working, Claude Desktop will display a `Cannot attach to MCP`, simply ignore it, Claude Desktop runs a ping every X seconds, and when it is running a long-running task, the ping will fail, but the task itself will still finish correctly.
|
|
2348
|
-
|
|
2349
|
-
**Failed to generate DXT package**:
|
|
2350
|
-
|
|
2351
|
-
If you encounter the following error running a command such as:
|
|
2352
|
-
|
|
2353
|
-
```bash
|
|
2354
|
-
rm -rf node_modules
|
|
2355
|
-
pnpm install --prod --node-linker=hoisted
|
|
2356
|
-
bun src/index.ts --s-build-dxt ./dxt --s-with-node-modules
|
|
2357
|
-
|
|
2358
|
-
-- Error generating DXT package: TSDown DXT build failed: EEXIST: file already exists, mkdir
|
|
2359
|
-
```
|
|
2360
|
-
|
|
2361
|
-
Then run:
|
|
2362
|
-
|
|
2363
|
-
```bash
|
|
2364
|
-
rm -rf ./dxt
|
|
2365
|
-
bun src/index.ts --s-build-dxt ./dxt --s-with-node-modules
|
|
2366
|
-
```
|
|
2367
|
-
|
|
2368
|
-
And it should work. TSDown is tasked to clean the outputDir first, but it won't if some files have been manually changed.
|
|
2369
|
-
|
|
2370
|
-
---
|
|
92
|
+
> 📖 **Full Documentation**: [docs/TUI.md](./docs/TUI.md) | [Component Reference](./docs/TUI_COMPONENTS.md)
|
|
2371
93
|
|
|
2372
94
|
## System Flags & Configuration
|
|
2373
95
|
|
|
2374
|
-
ArgParser includes built-in `--s-*` flags for development
|
|
2375
|
-
|
|
2376
|
-
| Flag | Description |
|
|
2377
|
-
| --------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
2378
|
-
| **MCP & DXT** | |
|
|
2379
|
-
| `--s-mcp-serve` | Starts the application in MCP server mode, exposing all tools. |
|
|
2380
|
-
| `--s-build-dxt [dir]` | Generates a complete, autonomous DXT package for Claude Desktop in the specified directory. |
|
|
2381
|
-
| `--s-with-node-modules` | Use with `--s-build-dxt`. Includes complete node_modules in DXT package for projects with native dependencies. |
|
|
2382
|
-
| `--s-mcp-transport <type>` | Overrides the MCP transport (`stdio`, `sse`, `streamable-http`). |
|
|
2383
|
-
| `--s-mcp-transports <json>` | Overrides transports with a JSON array for multi-transport setups. |
|
|
2384
|
-
| `--s-mcp-port <number>` | Sets the port for HTTP-based transports (`sse`, `streamable-http`). |
|
|
2385
|
-
| `--s-mcp-host <string>` | Sets the host address for HTTP-based transports. |
|
|
2386
|
-
| `--s-mcp-log-path <path>` | Sets the file path for MCP server logs (default: `./logs/mcp.log`). Overrides programmatic setting. |
|
|
2387
|
-
| **Configuration** | |
|
|
2388
|
-
| `--s-with-env <file>` | Loads configuration from a file (`.env`, `.json`, `.yaml`, `.toml`). CLI args take precedence. |
|
|
2389
|
-
| `--s-save-to-env <file>` | Saves the current arguments to a configuration file, perfect for templates. |
|
|
2390
|
-
| **Debugging** | |
|
|
2391
|
-
| `--s-debug` | Prints a detailed, step-by-step log of the argument parsing process. |
|
|
2392
|
-
| `--s-debug-print` | Exports the entire parser configuration to a JSON file for inspection. |
|
|
2393
|
-
| `--s-enable-fuzzy` | Enables fuzzy testing mode—a dry run that parses args but skips handler execution. |
|
|
2394
|
-
|
|
2395
|
-
---
|
|
2396
|
-
|
|
2397
|
-
## Changelog
|
|
2398
|
-
|
|
2399
|
-
### v2.13.0
|
|
2400
|
-
|
|
2401
|
-
**New Feature: Positional Arguments**
|
|
2402
|
-
|
|
2403
|
-
Added support for positional (trailing) arguments, enabling more natural CLI syntax:
|
|
2404
|
-
|
|
2405
|
-
```bash
|
|
2406
|
-
# Before: flags required
|
|
2407
|
-
workflow show --id 8fadf090-xxx
|
|
2408
|
-
|
|
2409
|
-
# After: positional syntax works too!
|
|
2410
|
-
workflow show 8fadf090-xxx
|
|
2411
|
-
```
|
|
2412
|
-
|
|
2413
|
-
Flags can now specify `positional: N` (1-indexed) to capture trailing arguments:
|
|
2414
|
-
|
|
2415
|
-
```typescript
|
|
2416
|
-
.addFlag({
|
|
2417
|
-
name: "id",
|
|
2418
|
-
type: "string",
|
|
2419
|
-
mandatory: true,
|
|
2420
|
-
options: ["--id"], // Still works as fallback
|
|
2421
|
-
positional: 1, // Captures first trailing arg
|
|
2422
|
-
description: "Resource ID",
|
|
2423
|
-
})
|
|
2424
|
-
```
|
|
2425
|
-
|
|
2426
|
-
Key features:
|
|
2427
|
-
- **Dual syntax**: Both `--flag value` and positional work interchangeably
|
|
2428
|
-
- **Precedence**: Flag syntax takes priority if both provided
|
|
2429
|
-
- **Multiple positional args**: Use `positional: 1`, `positional: 2`, etc.
|
|
2430
|
-
- **Help text enhancement**: Shows usage pattern like `Usage: cmd [OPTIONS] <ID>`
|
|
2431
|
-
- **Full validation**: Works with `mandatory`, type coercion, and enum validation
|
|
2432
|
-
|
|
2433
|
-
See [Positional Arguments](#positional-arguments) for complete documentation.
|
|
2434
|
-
|
|
2435
|
-
### v2.12.3
|
|
2436
|
-
|
|
2437
|
-
**Fixes**
|
|
2438
|
-
|
|
2439
|
-
- Make sure that when setWorkingDir is used, the newly discovered .env override process.env variables
|
|
2440
|
-
- Display subcommand descriptions on separate lines for better readability
|
|
2441
|
-
|
|
2442
|
-
### v2.12.2
|
|
2443
|
-
|
|
2444
|
-
**Fixes**
|
|
2445
|
-
|
|
2446
|
-
- Fix env config matching and improve working directory integration
|
|
2447
|
-
|
|
2448
|
-
Explicitly call dotenv.config when an env file is auto-discovered
|
|
2449
|
-
to populate process.env. This ensures flags with the 'env' property
|
|
2450
|
-
can bind values from the discovered file.
|
|
2451
|
-
|
|
2452
|
-
### v2.12.0
|
|
2453
|
-
|
|
2454
|
-
- Switch back to official @modelcontextprotocol/sdk as it now supports Zod V4, this resolves a security issue from a dependency in MCP SDK @ 1.16.0 branch.
|
|
2455
|
-
|
|
2456
|
-
### v2.11.0
|
|
2457
|
-
|
|
2458
|
-
Working Directory Management & OpenTUI v2 Framework
|
|
2459
|
-
|
|
2460
|
-
#### Working Directory Management (chdir)
|
|
2461
|
-
|
|
2462
|
-
A major new capability for monorepo support and complex project structures:
|
|
2463
|
-
|
|
2464
|
-
- **`setWorkingDirectory` Flag Property**: Designate any flag's value as the effective working directory. When used, `.env` file loading and relative path operations automatically resolve from this directory.
|
|
2465
|
-
- **`rootPath` in Handler Context**: Access the original working directory (where the user ran the command) via `ctx.rootPath`. Perfect for displaying user-friendly paths or resolving user-provided files relative to their PWD.
|
|
2466
|
-
- **Smart `.env` Auto-Discovery**: When used with `--s-with-env`, automatically discovers `.env.local`, `.env.dev`, `.env.test`, or `.env` in the effective working directory (priority order).
|
|
2467
|
-
- **Protected Validation**: Warnings for invalid paths (nonexistent, not a directory) and multiple workspace flags.
|
|
2468
|
-
|
|
2469
|
-
```typescript
|
|
2470
|
-
const parser = new ArgParser({
|
|
2471
|
-
appName: "Monorepo CLI",
|
|
2472
|
-
handler: async (ctx) => {
|
|
2473
|
-
console.log("Effective cwd:", process.cwd()); // Changed by --workspace
|
|
2474
|
-
console.log("User's cwd:", ctx.rootPath); // Original location
|
|
2475
|
-
},
|
|
2476
|
-
}).addFlag({
|
|
2477
|
-
name: "workspace",
|
|
2478
|
-
options: ["--workspace", "-w"],
|
|
2479
|
-
type: "string",
|
|
2480
|
-
setWorkingDirectory: true, // Makes this flag control the working directory
|
|
2481
|
-
});
|
|
2482
|
-
```
|
|
2483
|
-
|
|
2484
|
-
See [Working Directory Documentation](./docs/WORKING_DIRECTORY.md) for complete examples.
|
|
2485
|
-
|
|
2486
|
-
#### OpenTUI v2 - Complete TUI Rewrite
|
|
2487
|
-
|
|
2488
|
-
The TUI framework has been completely rewritten using **SolidJS** and **SST's OpenTUI** for a reactive, component-based architecture:
|
|
2489
|
-
|
|
2490
|
-
- **Reactive Components**: `TuiProvider`, `VirtualList`, `MasterDetail`, `Breadcrumb` built on SolidJS signals.
|
|
2491
|
-
- **Unified Provider**: `TuiProvider` handles mouse wheel reporting, terminal resize, TTY cleanup, and theme/shortcut contexts automatically.
|
|
2492
|
-
- **Rich Theme System**: 6 built-in themes (`dark`, `light`, `monokai`, `dracula`, `nord`, `solarized`) with `Theme.from().extend()` for custom themes.
|
|
2493
|
-
- **VirtualList**: Efficient virtualized scrolling with `createVirtualListController` for navigation control.
|
|
2494
|
-
- **Slot-Based Layouts**: `MasterDetail` component with header, breadcrumb, footer, and customizable panel widths.
|
|
2495
|
-
- **Hooks**: `useTui()` for viewport/exit, `useTheme()` for theming, plus mouse and virtual scroll hooks.
|
|
2496
|
-
- **TTY Utilities**: Exported `cleanupTerminal`, `enableMouseReporting`, etc. for custom terminal control.
|
|
2497
|
-
|
|
2498
|
-
```tsx
|
|
2499
|
-
import {
|
|
2500
|
-
MasterDetail,
|
|
2501
|
-
TuiProvider,
|
|
2502
|
-
useTui,
|
|
2503
|
-
VirtualList,
|
|
2504
|
-
} from "@alcyone-labs/arg-parser/tui";
|
|
2505
|
-
import { render } from "@opentui/solid";
|
|
2506
|
-
|
|
2507
|
-
function App() {
|
|
2508
|
-
const { viewportHeight, exit } = useTui();
|
|
2509
|
-
const [idx, setIdx] = createSignal(0);
|
|
2510
|
-
|
|
2511
|
-
return (
|
|
2512
|
-
<MasterDetail
|
|
2513
|
-
header="My App"
|
|
2514
|
-
master={
|
|
2515
|
-
<VirtualList items={DATA} selectedIndex={idx()} onSelect={setIdx} />
|
|
2516
|
-
}
|
|
2517
|
-
detail={<Details item={DATA[idx()]} />}
|
|
2518
|
-
/>
|
|
2519
|
-
);
|
|
2520
|
-
}
|
|
2521
|
-
|
|
2522
|
-
render(() => (
|
|
2523
|
-
<TuiProvider theme="dark" onScroll={(d) => setIdx((i) => i + d)}>
|
|
2524
|
-
<App />
|
|
2525
|
-
</TuiProvider>
|
|
2526
|
-
));
|
|
2527
|
-
```
|
|
2528
|
-
|
|
2529
|
-
See [TUI Documentation](./docs/TUI.md) for complete API reference and examples.
|
|
2530
|
-
|
|
2531
|
-
#### Other Improvements
|
|
2532
|
-
|
|
2533
|
-
- **Data-Safe Logging**: Integrated `@alcyone-labs/simple-mcp-logger` for STDOUT-safe logging.
|
|
2534
|
-
- **Bun Configuration**: Added `bunfig.toml` with OpenTUI preload for native JSX support.
|
|
2535
|
-
- **New Examples**: `aquaria-trace-viewer.tsx`, `framework-demo.tsx`, `template-demo.tsx`, `tui-demo-v2.tsx`.
|
|
2536
|
-
|
|
2537
|
-
### v2.10.3
|
|
2538
|
-
|
|
2539
|
-
**Flag Inheritance Improvements**
|
|
2540
|
-
|
|
2541
|
-
- **Full Chain Inheritance**: Introduced `FlagInheritance.AllParents` option to support deep flag propagation. This fixes issues where nested sub-commands (e.g., `root > mid > leaf`) failed to inherit root flags when constructed bottom-up.
|
|
2542
|
-
- **Granular Control**: New `FlagInheritance` configuration object provides clear options: `NONE`, `DirectParentOnly` (legacy behavior), and `AllParents`.
|
|
2543
|
-
- **Type Safety**: New `TFlagInheritance` type definition for better TypeScript support.
|
|
2544
|
-
- **Backward Compatibility**: Kept `inheritParentFlags` for legacy behavior, but now it's just an alias for `FlagInheritance.DirectParentOnly`.
|
|
2545
|
-
|
|
2546
|
-
**Auto-Help Features**
|
|
2547
|
-
|
|
2548
|
-
- **Programmatic Help**: Added `ctx.displayHelp()` method to command handlers, allowing easy help display from within your logic.
|
|
2549
|
-
- **Auto-Trigger**: Added `triggerAutoHelpIfNoHandler` option to automatically show help messages for "container" commands that don't have their own handler.
|
|
2550
|
-
- **Helper Function**: Exported `autoHelpHandler` utility for quick setup of help-only commands. This can be passed as `setHandler(autoHelpHandler)` or `handler: autoHelpHandler` depending on your API of choice.
|
|
2551
|
-
|
|
2552
|
-
### v2.10.2
|
|
2553
|
-
|
|
2554
|
-
**OpenTUI Improvements**
|
|
2555
|
-
|
|
2556
|
-
- **Soft Wrapping**: Added `wrapText` (boolean) to `ScrollArea` component. When enabled, text automatically reflows to fit the container width (preventing clipping).
|
|
2557
|
-
- **ANSI Preservation**: Soft-wrapping logic is ANSI-aware; color and style states are correctly carried over to wrapped lines.
|
|
2558
|
-
|
|
2559
|
-
### v2.10.1
|
|
2560
|
-
|
|
2561
|
-
- **Bug Fixes**:
|
|
2562
|
-
- Fixed a crash in `Terminal.moveCursor` when running in certain environments where `node:readline` utilities were inaccessible. Switched to direct ANSI escape codes for cursor positioning.
|
|
2563
|
-
|
|
2564
|
-
### v2.10.0 - OpenTUI integration + IFlag "env" property now first-class citizen
|
|
2565
|
-
|
|
2566
|
-
#### OpenTUI Integration
|
|
2567
|
-
|
|
2568
|
-
- **OpenTUI**: Integrated a complete Terminal User Interface (TUI) framework into the library core.
|
|
2569
|
-
- **StackNavigator**: Standardized UX for deep navigation with `Enter`/`Right` to push and `Esc`/`Left` to pop views.
|
|
2570
|
-
- **Reactive Themes**: Centralized `ThemeManager` with `Default`, `Ocean` (High-Contrast), and `Monokai` presets.
|
|
2571
|
-
- **Scroll Performance**: ANSI-aware left-side scrollbars with automatic height calculation and scroll-state management.
|
|
2572
|
-
- **Mouse Integration**: Native SGR mouse reporting for wheel scrolling and hit-detected clicks.
|
|
2573
|
-
- **Safety**: Robust TTY restoration and process-level cleanup to prevent terminal lockups on exit or crash.
|
|
2574
|
-
- **Components**: Exported `List`, `ScrollArea`, `Input`, and `SplitLayout` under the `UI` namespace.
|
|
2575
|
-
|
|
2576
|
-
#### Universal Environment Variable Support
|
|
2577
|
-
|
|
2578
|
-
- **Universal `env` Support**: The `env` property is now a core feature available to all commands and tools, not just DXT/MCP contexts.
|
|
2579
|
-
- **Resolution Priority**: Implemented strict precedence: **CLI Flag > Environment Variable > Default Value**.
|
|
2580
|
-
- **Reverse Sync**: Resolved flag values (from CLI or Env) are now automatically synced back to `process.env`, ensuring downstream code sees the correct configuration.
|
|
2581
|
-
- **Flexible Mapping**: Supports both string and array-of-strings for `env` (first match wins).
|
|
2582
|
-
- **Automatic Type Conversion**: Environment variables are automatically coerced to the flag's defined type (Number, Boolean, etc.).
|
|
2583
|
-
|
|
2584
|
-
### v2.8.2
|
|
2585
|
-
|
|
2586
|
-
- UX: Help shows example values via `valueHint` for non-boolean flags; repeatable flags display 'Multiple values allowed (repeat flag)' with example; examples use `valueHint` when present.
|
|
2587
|
-
- Types: Added `IFlag.valueHint?: string`; accepted by `zodFlagSchema`; included in processed flags; supported in manifest-driven dynamic flags.
|
|
2588
|
-
- Examples: `examples/core/dynamic-flags-demo.ts` updated to demonstrate `valueHint` for `--url`.
|
|
2589
|
-
|
|
2590
|
-
### v2.8.1
|
|
2591
|
-
|
|
2592
|
-
- Feature: Dynamic flags via `IFlag.dynamicRegister(ctx)` to register additional flags at runtime (e.g., from a manifest file)
|
|
2593
|
-
- Help: `--help` preloads dynamic flags without executing handlers; help output includes both static and dynamic flags
|
|
2594
|
-
- Flow: Two-phase parsing (load dynamic flags → re-parse with full flag set)
|
|
2595
|
-
- Cleanup: Dynamically registered flags are reset between parses to avoid accumulation
|
|
2596
|
-
- Types: Exported `DynamicRegisterContext` and `DynamicRegisterFn`
|
|
2597
|
-
- Internal: `FlagManager.removeFlag(name)` to support cleanup
|
|
2598
|
-
|
|
2599
|
-
### v2.7.2
|
|
2600
|
-
|
|
2601
|
-
**Feat**
|
|
2602
|
-
|
|
2603
|
-
**MCP**:
|
|
2604
|
-
|
|
2605
|
-
- outputSchema is now included in MCP tool registration for MCP 2025-06-18+ clients and will generate a JSON Schema in `tools/list` responses to make JSON introspection easier.
|
|
2606
|
-
|
|
2607
|
-
**Fixes and Changes**
|
|
2608
|
-
|
|
2609
|
-
**MCP**:
|
|
2610
|
-
|
|
2611
|
-
- The app parameter in configureExpress: (app) => {} is now fully typed to improve intellisense.
|
|
2612
|
-
- Express' x-powered-by was disabled by default. It can be re-enabled or changed via configureExpress as needed.
|
|
2613
|
-
- Logger parameters were still not fully functional and log level was still ignored, this has been fixed.
|
|
2614
|
-
|
|
2615
|
-
### v2.7.0
|
|
2616
|
-
|
|
2617
|
-
**Feat**
|
|
2618
|
-
|
|
2619
|
-
**MCP**:
|
|
2620
|
-
|
|
2621
|
-
- Add support for CORS and authentication options, enabling powerful tools to serve Web UIs and publicly exposed APIs
|
|
2622
|
-
- Add supports for configuring express by exposing the app before it runs
|
|
2623
|
-
|
|
2624
|
-
**CLI**:
|
|
2625
|
-
|
|
2626
|
-
- Add support for NOT automatically executing the CLI if the script is imported, but will execute if called directly as a CLI. This enables use cases such as programmatically loading the CLI and scanning for tools or testing it from another script via the --s-enable-fuzzy flag or your own script.
|
|
2627
|
-
|
|
2628
|
-
### v2.6.0
|
|
2629
|
-
|
|
2630
|
-
**Feat**
|
|
2631
|
-
|
|
2632
|
-
**DXT**:
|
|
2633
|
-
|
|
2634
|
-
- Improve how paths and dynamic variables are handled when bundling into a DXT, to improve compatibility and reduce paths that will fail in a sandbox when the CLI / MCP expects path available on the system. Dynamic path resolution with `${VARIABLE}` syntax supporting `${HOME}`, `${DOCUMENTS}`, `${__dirname}`, `${DXT_DIR}`, and more. Context-aware path resolution with `DxtPathResolver.createUserDataPath()`, `createTempPath()`, `createConfigPath()`.
|
|
2635
|
-
- Add new IFlag.dxtOption set of options for each flag to allow finer control on how the flags are perceived on the DXT manifest / Claude Desktop.
|
|
2636
|
-
|
|
2637
|
-
Read more her: [DXT Package User Configuration & Path Handling](#dxt-package-user-configuration--path-handling)
|
|
2638
|
-
|
|
2639
|
-
**Fixes and Changes**
|
|
2640
|
-
|
|
2641
|
-
**DXT**:
|
|
2642
|
-
|
|
2643
|
-
- Improve handling of sensitive env variable, they were previously always showing as sensitive.
|
|
2644
|
-
|
|
2645
|
-
**Known Limitations**
|
|
2646
|
-
|
|
2647
|
-
**DXT**:
|
|
2648
|
-
|
|
2649
|
-
The DXT bundling / packing / unpacking / launching process is notoriously early and brittle. There are many reasons something is not working, but **MOST** importantly it will not work if:
|
|
2650
|
-
|
|
2651
|
-
1. You are bundling a package in a mono-repo (you will need to temporarily create a pnpm-workspace.yaml file for example to break the hierarchy)
|
|
2652
|
-
2. You do _not_ hard-install your node_modules as detailed in the documentation (it will only work if the node_modules are hard installed)
|
|
2653
|
-
3. In some cases if your CLI entrypoint does not run a main loop (see documentation for working examples)
|
|
2654
|
-
4. If you use PATH-dependent variables (for example relying on ~/.config/path/to/some.json). This has been addressed in v2.6.0, but you have to make sure you use the correct patterns (see documentation)
|
|
2655
|
-
|
|
2656
|
-
### v2.5.0
|
|
2657
|
-
|
|
2658
|
-
**Feat**
|
|
2659
|
-
|
|
2660
|
-
- **Zod Schema Flags**: You can now use Zod schemas as flag types for structured JSON input validation. This enables complex object validation with automatic JSON Schema generation for MCP clients while maintaining full type safety and CLI compatibility.
|
|
2661
|
-
- **Improved MCP Tool Documentation**: Zod schema descriptions automatically become MCP tool parameter documentation
|
|
2662
|
-
|
|
2663
|
-
### v2.4.2
|
|
2664
|
-
|
|
2665
|
-
**Fixes and Changes**
|
|
2666
|
-
|
|
2667
|
-
- add missing MCP lifecycle event documentation
|
|
2668
|
-
- fix the behavior of the withMcp() options.mcp.log that was not working as expected
|
|
2669
|
-
|
|
2670
|
-
### v2.4.1
|
|
2671
|
-
|
|
2672
|
-
**Fixes and Changes**
|
|
2673
|
-
|
|
2674
|
-
- switch to NPM version of @alcyone-labs/modelcontextprotocol-sdk to freeze the dependency and avoid side-effects
|
|
2675
|
-
|
|
2676
|
-
### v2.4.0
|
|
2677
|
-
|
|
2678
|
-
**Feat**
|
|
2679
|
-
|
|
2680
|
-
- MCP client now supports initialization during the client 'initialize' call, which allows to do certain things such as establishing database connection or even running migrations
|
|
2681
|
-
- MCP client now sanitizes the method names to ensure spec-compliants MCP behavior, names that collision will be logged
|
|
2682
|
-
- There were some use-cases where the DXT bundling failed, this new release addresses all of them, namely:
|
|
2683
|
-
1. Output structure will match that of the input so relative files (for example DB migrations) will work
|
|
2684
|
-
2. Deeper folder structure was previously not working
|
|
2685
|
-
- DXT bundling now supports including resources via options: `{dxt: {include: ['TSDown blob-like paths']}`
|
|
2686
|
-
- Logger was improved to support log level via `options:{ log: {} }` so you can set it to level: 'debug' and the MCP log will contain 100% of the console output, logPath setting was not impacted
|
|
2687
|
-
|
|
2688
|
-
**Fixes and Changes**
|
|
2689
|
-
|
|
2690
|
-
- Zod has been upgraded to V4 and no issue was identified (but @modelcontextprotocol/sdk had to be upgraded to V4 as well, which was more challenging).
|
|
2691
|
-
|
|
2692
|
-
### v2.3.0
|
|
2693
|
-
|
|
2694
|
-
The DXT bundling is working pretty well now, and we have had a lot of success building, bundling and running various extensions. If you see issues, feel free to open an Issue on GitHub with details, or ask about it on [Alcyone Labs' Discord](https://discord.gg/rRHhpz5nS5)
|
|
2695
|
-
|
|
2696
|
-
Make sure to clearly identify if you need to include the node_modules or not. In doubt, include them using `--s-with-node-modules`
|
|
2697
|
-
|
|
2698
|
-
**Feat**
|
|
2699
|
-
|
|
2700
|
-
- **New `--s-with-node-modules` flag**: Create fully autonomous DXT packages that include complete native dependencies. Perfect for projects using ONNX Runtime, Sharp, SQLite, or other packages with `.node` binaries. Use `rm -rf ./node_modules && pnpm install --prod --node-linker=hoisted` followed by `my-cli --s-build-dxt ./dxt --s-with-node-modules` to create self-contained packages that work without Claude needing to install dependencies.
|
|
2701
|
-
Note that when bundling with node_modules, it's likely that the built-in Node.js will not work with that extension, so go to `Claude Desktop > Settings > Extensions > Advanced Settings` and turn **OFF** `Use Built-in Node.js for MCP`.
|
|
2702
|
-
|
|
2703
|
-
### v2.2.1
|
|
2704
|
-
|
|
2705
|
-
**Feat**
|
|
2706
|
-
|
|
2707
|
-
- You can now specify logPath for the MCP output and easily disambiguate what the path is relative to (`__dirname` versus `process.cwd()` versus absolute)
|
|
2708
|
-
|
|
2709
|
-
**Fixes and changes**
|
|
2710
|
-
|
|
2711
|
-
- Fixes an issue where building a DXT package via `--s-build-dxt` would generate an invalid package if the entry_point was a TypeScript .ts file.
|
|
2712
|
-
|
|
2713
|
-
### v2.2.0
|
|
2714
|
-
|
|
2715
|
-
**Feat**
|
|
2716
|
-
|
|
2717
|
-
- IFlag function-based `type` now supports async methods such as `type: async () => Promise<string>`.
|
|
2718
|
-
|
|
2719
|
-
**Fixes and changes**
|
|
2720
|
-
|
|
2721
|
-
- `.parse()` can now work without arguments, it will try to infer that if you are in CLI mode and on a Node environment, it should use `process.argv` as the input. You can still pass parameters to control more granularly.
|
|
2722
|
-
- `--s-build-dxt` now takes an optional path to specify where to prepare the assets prior to packing, the path you pass is in relation to process.cwd() (current working directory).
|
|
2723
|
-
- `--s-build-dxt` logo detection now resolves paths more accurately...
|
|
2724
|
-
|
|
2725
|
-
### v2.1.1
|
|
2726
|
-
|
|
2727
|
-
**Fixes and changes**
|
|
2728
|
-
|
|
2729
|
-
- Fix missing missing types fr
|
|
2730
|
-
|
|
2731
|
-
### v2.1.0
|
|
2732
|
-
|
|
2733
|
-
**Feat**
|
|
2734
|
-
|
|
2735
|
-
- IFlag function-based `type` handling must now define the type it returns, this unlocks nice features such as providing nicer Intellisense, `output schemas` support and makes it easier to upgrade to Zod V4
|
|
2736
|
-
- Add support for MCP output_schema field for clients that support it, CLI isn't impacted by it, this helps a lot the interactivity, self-documentation, and improves the API guarantees
|
|
2737
|
-
|
|
2738
|
-
**Fixes and changes**
|
|
2739
|
-
|
|
2740
|
-
- Improved MCP version compliance
|
|
2741
|
-
|
|
2742
|
-
### v2.0.0
|
|
2743
|
-
|
|
2744
|
-
- **Unified Tool Architecture**: Introduced `.addTool()` to define CLI subcommands and MCP tools in a single declaration.
|
|
2745
|
-
- **Universal Environment Variables Support**: The `env` property on any `IFlag` now provides native `process.env` fallback/sync logic for all commands, while maintaining its role in generating `user_config` entries for DXT manifests.
|
|
2746
|
-
- **Enhanced DXT Generation**: The `env` property on flags now automatically generates `user_config` entries in the DXT manifest.
|
|
2747
|
-
- **Automatic Console Safety**: Console output is automatically and safely redirected in MCP mode to prevent protocol contamination.
|
|
2748
|
-
- **Breaking Changes**: The `addMcpSubCommand()` and separate `addSubCommand()` for MCP tools are deprecated in favor of `addTool()` and `--s-mcp-serve`.
|
|
2749
|
-
|
|
2750
|
-
### v1.3.0
|
|
2751
|
-
|
|
2752
|
-
- **Plugin System & Architecture**: Refactored to a dependency-injection model, making the core library dependency-free. Optional plugins for TOML/YAML.
|
|
2753
|
-
- **Global Console Replacement**: Implemented the first version of automatic console suppression for MCP compliance.
|
|
2754
|
-
- **Autonomous Build Improvements**: Significantly reduced DXT bundle size and removed dynamic `require` issues.
|
|
2755
|
-
|
|
2756
|
-
### v1.2.0
|
|
2757
|
-
|
|
2758
|
-
- **Critical MCP Fixes**: Resolved issues where MCP tools with output schemas would fail. Ensured correct JSON-RPC 2.0 response formatting.
|
|
2759
|
-
- **Enhanced Handler Context**: Added `isMcp` flag to the handler context for more reliable mode detection.
|
|
2760
|
-
|
|
2761
|
-
### v1.1.0
|
|
2762
|
-
|
|
2763
|
-
- **Major Features**: First release with MCP Integration, System Flags (`--s-debug`, `--s-with-env`, etc.), and environment loading from files.
|
|
2764
|
-
|
|
2765
|
-
---
|
|
2766
|
-
|
|
2767
|
-
## Backlog
|
|
96
|
+
ArgParser includes built-in `--s-*` flags for development and debugging.
|
|
2768
97
|
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
-
|
|
2775
|
-
-
|
|
2776
|
-
- [ ] Add System flags to args.systemArgs
|
|
2777
|
-
- [ ] Improve flag options collision prevention
|
|
2778
|
-
- [ ] (potentially) add support for fully typed parsed output, this has proven very challenging
|
|
2779
|
-
- [ ] Add support for locales / translations
|
|
98
|
+
| Flag | Description |
|
|
99
|
+
| --------------------------- | --------------------------------------------------------------------------- |
|
|
100
|
+
| `--s-mcp-serve` | Starts the application in MCP server mode. |
|
|
101
|
+
| `--s-build-dxt [dir]` | Generates a DXT package for Claude Desktop. |
|
|
102
|
+
| `--s-with-env <file>` | Loads configuration from a file (`.env`, `.json`, etc.). |
|
|
103
|
+
| `--s-save-to-env <file>` | Saves current arguments to a configuration file. |
|
|
104
|
+
| `--s-debug` | Prints a detailed log of the argument parsing process. |
|
|
2780
105
|
|
|
2781
|
-
|
|
106
|
+
## Links
|
|
2782
107
|
|
|
2783
|
-
- [
|
|
108
|
+
- 📜 **[Changelog](./CHANGELOG.md)**
|
|
109
|
+
- 📋 **[Backlog](./BACKLOG.md)**
|
|
110
|
+
- 💬 **[Discord Support](https://discord.gg/rRHhpz5nS5)**
|