npm package: @modeltoolsprotocol/sdk
Description: Add MTP --describe support to any Commander.js CLI
Latest version: 0.5.0

# @modeltoolsprotocol/sdk

Add MTP `--mtp-describe` support to any Commander.js CLI. One function call, zero boilerplate.

## Install

```bash
npm install @modeltoolsprotocol/sdk commander
```

## Quick Start

```typescript
import { Command } from "commander";
import { withDescribe } from "@modeltoolsprotocol/sdk";

const program = new Command()
  .name("filetool")
  .version("1.0.0")
  .description("Convert and validate files");

program
  .command("convert")
  .argument("<input>", "Input file path")
  .option("--format <fmt>", "Output format", "json")
  .option("--pretty", "Pretty-print output")
  .action((input, opts) => { /* ... */ });

withDescribe(program, {
  commands: {
    convert: {
      stdin: { contentType: "text/plain", description: "Raw input" },
      stdout: { contentType: "application/json" },
      examples: [
        { description: "Convert CSV", command: "filetool convert data.csv --format json" },
      ],
    },
  },
});

program.parse();
```

```bash
$ filetool --mtp-describe          # MTP JSON schema
$ filetool convert data.csv    # normal operation
```

## API

### `withDescribe(program, options?)`

Adds `--mtp-describe` to an existing Commander program. When invoked, outputs MTP-compliant JSON and exits.

- **program** — a Commander `Command` instance (your root program)
- **options.commands** — per-command annotations keyed by command name (stdin, stdout, examples, argTypes)
- **options.auth** — authentication config to include in the schema
- Returns the program for chaining

### `describe(program, options?)`

Pure function. Returns the `ToolSchema` object without side effects. Useful for testing or programmatic access.

## How It Works

The SDK introspects Commander's own data structures — `cmd.options`, `cmd.registeredArguments`, `cmd.commands` — so you never duplicate information. Supplemental metadata (stdin/stdout/examples) that Commander doesn't model is provided via the options map.

### Type Inference

Arg types describe flags and positional arguments, which are always scalar on the command line:

| Commander signal | MTP type |
|---|---|
| `option.isBoolean()` | `"boolean"` |
| `option.argChoices` | `"enum"` + `values` |
| `option.variadic` / `arg.variadic` | `"array"` |
| explicit `argTypes` override | whatever you say |
| everything else | `"string"` |

For structured data flowing through stdin/stdout, use the `schema` field in IO descriptors. This supports full JSON Schema (draft 2020-12): nested objects, arrays, unions, pattern validation, conditional fields.

### Structured IO

When a command accepts or produces JSON, describe the shape with a JSON Schema:

```typescript
withDescribe(program, {
  commands: {
    process: {
      stdin: {
        contentType: "application/json",
        description: "Configuration to process",
        schema: {
          type: "object",
          properties: {
            name: { type: "string" },
            settings: {
              type: "object",
              properties: {
                retries: { type: "integer" },
                endpoints: { type: "array", items: { type: "string", format: "uri" } },
              },
            },
          },
          required: ["name"],
        },
      },
      stdout: {
        contentType: "application/json",
        schema: {
          type: "object",
          properties: {
            status: { type: "string", enum: ["ok", "error"] },
            results: { type: "array", items: { type: "object" } },
          },
        },
      },
    },
  },
});
```

### Command Naming

- Programs with subcommands: each leaf command gets a space-separated path (e.g., `"auth login"`)
- Programs with no subcommands: single command named `"_root"`
- Hidden commands and options are excluded

### Filtered Options

These are automatically excluded from schema output: `--help`, `--version`, `--mtp-describe`, and any hidden options.

## Single-Command Tools

Tools with no subcommands work the same way:

```typescript
import { Command } from "commander";
import { withDescribe } from "@modeltoolsprotocol/sdk";

const program = new Command()
  .name("greet")
  .version("1.0.0")
  .description("Greet someone")
  .argument("<name>", "Name to greet")
  .option("--loud", "Shout the greeting")
  .action((name, opts) => {
    const msg = `Hello, ${name}!`;
    console.log(opts.loud ? msg.toUpperCase() : msg);
  });

withDescribe(program);
program.parse();
```

This produces a schema with a single `_root` command.