Allow positional inline commands for generate-cli

This commit is contained in:
Peter Steinberger 2025-11-07 14:51:47 +00:00
parent b5e1c5df84
commit 002cb8cbc8
7 changed files with 52 additions and 5 deletions

View File

@ -18,7 +18,7 @@
- Generated CLIs now embed their metadata (generator version, resolved server definition, invocation flags) behind a hidden `__mcporter_inspect` command. `mcporter inspect-cli` / `mcporter generate-cli --from <artifact>` read directly from the artifact, while legacy `.metadata.json` sidecars remain as a fallback for older binaries.
- Shared the TypeScript signature formatter between `mcporter list` and `mcporter generate-cli`, ensuring command summaries, CLI hints, and generator help stay pixel-perfect and are backed by new snapshot/unit tests.
- Introduced `mcporter emit-ts`, a codegen command that emits `.d.ts` tool interfaces or ready-to-run client wrappers (`--mode types|client`, `--include-optional`) using the same doc/comment data that powers the CLI, so agents/tests can consume MCP servers with strong TypeScript types.
- `mcporter generate-cli` now accepts inline stdio commands via `--command "npx -y package@latest"`, automatically splits the command/args, infers a friendly name from scripts or package scopes, and documents the chrome-devtools one-liner in the README; additional unit tests cover HTTP, stdio, and scoped package shorthands.
- `mcporter generate-cli` now accepts inline stdio commands via `--command "npx -y package@latest"` or by quoting the command as the first positional argument, automatically splits the command/args, infers a friendly name from scripts or package scopes, and documents the chrome-devtools one-liner in the README; additional unit tests cover HTTP, stdio, scoped package, and positional shorthand flows.
### Documentation & references
- Added `docs/tool-calling.md`, `docs/call-syntax.md`, and `docs/call-heuristic.md` to capture every invocation style (flags, function expressions, inferred verbs) plus the typo-correction rules.

View File

@ -274,6 +274,8 @@ npx mcporter generate-cli \
>
> `npx mcporter generate-cli --command "npx -y chrome-devtools-mcp@latest"`
Tip: you can drop `--command` when the inline command is the first positional argument (e.g., `npx mcporter generate-cli "npx -y chrome-devtools-mcp@latest"`).
- `--name` overrides the inferred CLI name.
- Add `--description "..."` if you want a custom summary in the generated help output.
- Add `--bundle [path]` to emit an esbuild bundle alongside the template.

View File

@ -79,6 +79,7 @@ npx mcporter generate-cli --command "npx -y chrome-devtools-mcp@latest"
- Omit `--name` to let mcporter infer it from the command URL (for example, `https://mcp.context7.com/mcp` becomes `context7`).
- When targeting an existing config entry, you can skip `--server` and pass the name as a positional argument:
`npx mcporter generate-cli linear --bundle dist/linear.js`.
- When the MCP server is a stdio command, you can also skip `--command` by quoting the inline command as the first positional argument (e.g., `npx mcporter generate-cli "npx -y chrome-devtools-mcp@latest"`).
```

View File

@ -27,7 +27,7 @@ A quick reference for the primary `mcporter` subcommands. Each command inherits
compiling with Bun).
- Key flags:
- `--server <name>` (or inline JSON) choose the server definition.
- `--command <url|command>` point at an ad-hoc HTTP endpoint or a stdio command (e.g., `"npx -y chrome-devtools-mcp@latest"`); mcporter infers the name when omitted.
- `--command <url|command>` point at an ad-hoc HTTP endpoint or a stdio command (e.g., `"npx -y chrome-devtools-mcp@latest"`); mcporter infers the name when omitted. Quoting the command as the first positional argument works too, so `npx mcporter generate-cli "npx -y chrome-devtools-mcp@latest"` is equivalent.
- `--output <path>` where to write the TypeScript template.
- `--bundle <path>` emit a bundle (Node/Bun) ready for `bun x`.
- `--compile <path>` compile with Bun (implies `--runtime bun`).

View File

@ -18,7 +18,7 @@ summary: 'Plan for the mcporter package replacing the Sweetistics pnpm MCP helpe
- Typed utilities for env/header resolution and stdio command execution.
- CLI entry point (`npx mcporter list|call`) built on the same runtime with configurable log levels (`--log-level` flag, `MCPORTER_LOG_LEVEL` env) defaulting to `warn`. Single-server listings must render as TypeScript-style headers: dimmed `/** ... */` doc blocks followed by `function name(...)` signatures, inferred return annotations when schemas expose a `title`, inline enum/format hints, and an optional-parameter summary (`// optional (N): a, b, ...`). Optional fields are hidden by default (unless there are ≤2 of them and <4 required parameters) and the CLI should tell users to run `--all-parameters` whenever anything is suppressed. The CLI should also infer the verb when users omit it: bare server names run `list <server>` (with the same typo-friendly heuristic used by `mcporter list`), while dotted tokens such as `linear.list_issues` dispatch to `call` automatically. Anonymous HTTP MCP servers (e.g., shadcn) must be auto-detected: if an ad-hoc URL returns MCP-shaped JSON even with a non-200 status, mcporter treats it as authenticated instead of launching the OAuth flow. Likewise, `call` must recognise HTTP selectors that inline the tool name (e.g., `https://www.shadcn.io/api/mcp.getComponent(...)`), strip the `.tool` suffix to derive the base server, parse any function-call arguments, and reuse an existing definition when the base URL matches a configured server. Selectors may omit the protocol entirelywe assume HTTPS when no scheme is present and treat hosts that only differ by a leading `www.` as identical for reuse purposes. See [docs/cli-reference.md](./cli-reference.md) for day-to-day usage/flag details.
- CLI generator (`npx mcporter generate-cli`) that emits standalone CLIs (plain TypeScript or bundled JS) with embedded schemas and Commander-based subcommands, targeting Node or Bun.
- CLI generator (`npx mcporter generate-cli`) that emits standalone CLIs (plain TypeScript or bundled JS) with embedded schemas and Commander-based subcommands, targeting Node or Bun. It must accept server references by name (positional), JSON, HTTP URL (with optional `.tool` suffix / missing scheme), or inline stdio commands (split into `command` + `args` so invocations like `--command "npx -y chrome-devtools-mcp@latest"` work without a config entry) just like the main CLI.
- CLI generator (`npx mcporter generate-cli`) that emits standalone CLIs (plain TypeScript or bundled JS) with embedded schemas and Commander-based subcommands, targeting Node or Bun. It must accept server references by name (positional), JSON, HTTP URL (with optional `.tool` suffix / missing scheme), or inline stdio commands (split into `command` + `args` so invocations like `--command "npx -y chrome-devtools-mcp@latest"` or positional equivalents work without a config entry) just like the main CLI.
- Test harness using the Sweetistics MCP fixtures to validate every configured server definition.
- Documentation: README, usage examples, migration guide for replacing `pnpm mcp:*`.
@ -47,7 +47,7 @@ summary: 'Plan for the mcporter package replacing the Sweetistics pnpm MCP helpe
- Back the proxy with targeted unit tests that cover primitive-only calls, positional tuples + option bags, and error fallbacks when schemas are missing.
## Standalone CLI Generation
- `generate-cli` should accept inline JSON, file paths, inline stdio commands, or existing config names and produce a ready-to-run CLI that maps tools to Commander subcommands.
- `generate-cli` should accept inline JSON, file paths, inline stdio commands (either via `--command` or as the first positional argument), or existing config names and produce a ready-to-run CLI that maps tools to Commander subcommands.
- Embed schemas (via `listTools { includeSchema: true }`) directly in the generated source so repeat executions avoid additional metadata calls.
- Support optional bundling through esbuild, producing Node-friendly `.cjs` files or Bun-ready `.js` binaries with executable shebangs.
- Surface flags for output path, runtime target (`node` or `bun`), bundle destination, and per-call timeout (default 30s).

View File

@ -40,7 +40,9 @@ export async function handleGenerateCli(args: string[], globalFlags: FlagMap): P
if (position !== -1) {
args.splice(position, 1);
}
if (looksLikeHttpUrl(positional) || positional.includes('://')) {
if (looksLikeInlineCommand(positional)) {
parsed.command = normalizeCommandInput(positional);
} else if (looksLikeHttpUrl(positional) || positional.includes('://')) {
parsed.command = positional;
} else {
parsed.server = positional;
@ -295,6 +297,23 @@ function parseGenerateFlags(args: string[]): GenerateFlags {
index += 1;
}
if (!server && !command && !from) {
const positional = args.find((token) => token && !token.startsWith('--'));
if (positional) {
const position = args.indexOf(positional);
if (position !== -1) {
args.splice(position, 1);
}
if (looksLikeInlineCommand(positional)) {
command = normalizeCommandInput(positional);
} else if (looksLikeHttpUrl(positional) || positional.includes('://')) {
command = positional;
} else {
server = positional;
}
}
}
return {
server,
name,
@ -395,6 +414,21 @@ function stripExtension(value: string): string {
return value.slice(0, index);
}
function looksLikeInlineCommand(value: string): boolean {
if (!value) {
return false;
}
if (!/\s/.test(value)) {
return false;
}
try {
const parts = splitCommandLine(value.trim());
return parts.length > 0;
} catch {
return false;
}
}
function deriveNameFromUrl(url: URL): string | undefined {
const genericHosts = new Set(['www', 'api', 'mcp', 'service', 'services', 'app', 'localhost']);
const knownTlds = new Set(['com', 'net', 'org', 'io', 'ai', 'app', 'dev', 'co', 'cloud']);

View File

@ -69,6 +69,16 @@ describe('generate-cli runner internals', () => {
expect(inferred).toBe('shadcn');
});
it('treats positional inline commands as generate-cli targets', () => {
const args = ['npx -y chrome-devtools-mcp@latest'];
const parsed = generateInternals.parseGenerateFlags([...args]);
expect(parsed.command).toBeDefined();
expect(parsed.server).toBeUndefined();
const spec = parsed.command as { command: string; args?: string[] };
expect(spec.command).toBe('npx');
expect(spec.args).toEqual(['-y', 'chrome-devtools-mcp@latest']);
});
it('builds regenerate commands honoring global flags and invocation overrides', () => {
const definition: SerializedServerDefinition = {
name: 'demo',