Polish CLI argument parsing and clean lint warnings
This commit is contained in:
parent
32a8c161a5
commit
189fca2d42
@ -13,7 +13,7 @@
|
||||
- Generated CLIs now show full command signatures in help and support `--compile` without leaving template/bundle intermediates.
|
||||
- StdIO-backed MCP servers now receive resolved environment overrides, so API keys flow through to launched processes like `obsidian-mcp-server`.
|
||||
- Hardened the CLI generator to surface enum defaults/metadata and added regression tests around the new helper utilities.
|
||||
- Fixed `mcporter call <server> <tool>` so the second positional is treated as the tool name instead of triggering the "Argument must be key=value" error; positional and `tool=` selectors now play nicely with additional key=value payloads.
|
||||
- Fixed `mcporter call <server> <tool>` so the second positional is treated as the tool name instead of triggering the "Argument must be key=value" error, and accepted `tool=`/`command=` selectors now play nicely with additional key=value payloads.
|
||||
|
||||
## [0.1.0]
|
||||
|
||||
|
||||
@ -4,9 +4,9 @@ import fsPromises from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js';
|
||||
import type { ServerSource } from './config.js';
|
||||
import { generateCli } from './generate-cli.js';
|
||||
import { createRuntime } from './runtime.js';
|
||||
import type { ServerSource } from './config.js';
|
||||
|
||||
type FlagMap = Partial<Record<string, string>>;
|
||||
|
||||
@ -584,7 +584,7 @@ export function parseCallArguments(args: string[]): CallArgsParseResult {
|
||||
throw new Error(`Argument '${token}' must be key=value format.`);
|
||||
}
|
||||
const value = coerceValue(raw);
|
||||
if (key === 'tool' && !result.tool) {
|
||||
if ((key === 'tool' || key === 'command') && !result.tool) {
|
||||
if (typeof value !== 'string') {
|
||||
throw new Error("Argument 'tool' must be a string value.");
|
||||
}
|
||||
|
||||
@ -796,7 +796,8 @@ function pickExampleValue(option: GeneratedOption): string {
|
||||
return option.exampleValue;
|
||||
}
|
||||
if (option.enumValues && option.enumValues.length > 0) {
|
||||
return option.enumValues[0]!;
|
||||
const [first] = option.enumValues;
|
||||
return first ?? option.property;
|
||||
}
|
||||
switch (option.type) {
|
||||
case 'number':
|
||||
@ -870,12 +871,6 @@ async function bundleOutput({
|
||||
return absTarget;
|
||||
}
|
||||
|
||||
function replaceExtension(file: string, extension: string): string {
|
||||
const dirname = path.dirname(file);
|
||||
const basename = path.basename(file, path.extname(file));
|
||||
return path.join(dirname, `${basename}.${extension}`);
|
||||
}
|
||||
|
||||
async function compileBundleWithBun(bundlePath: string, outputPath: string): Promise<void> {
|
||||
const bunBin = await verifyBunAvailable();
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
|
||||
@ -20,6 +20,14 @@ describe('CLI call argument parsing', () => {
|
||||
expect(parsed.args).toEqual({});
|
||||
});
|
||||
|
||||
it('treats command=NAME tokens as a tool alias for compatibility', async () => {
|
||||
const { parseCallArguments } = await cliModulePromise;
|
||||
const parsed = parseCallArguments(['chrome-devtools', 'command=list_pages']);
|
||||
expect(parsed.selector).toBe('chrome-devtools');
|
||||
expect(parsed.tool).toBe('list_pages');
|
||||
expect(parsed.args).toEqual({});
|
||||
});
|
||||
|
||||
it('retains key=value arguments after the selector and tool', async () => {
|
||||
const { parseCallArguments } = await cliModulePromise;
|
||||
const parsed = parseCallArguments(['chrome-devtools', 'list_pages', 'timeout=500']);
|
||||
|
||||
@ -41,6 +41,9 @@ describe('command string parsing', () => {
|
||||
|
||||
expect(servers).toHaveLength(1);
|
||||
const server = servers[0];
|
||||
if (!server) {
|
||||
throw new Error('expected server definition');
|
||||
}
|
||||
expect(server.command.kind).toBe('stdio');
|
||||
if (server.command.kind !== 'stdio') {
|
||||
throw new Error('expected stdio command');
|
||||
@ -70,11 +73,15 @@ describe('command string parsing', () => {
|
||||
})
|
||||
);
|
||||
|
||||
const [server] = await loadServerDefinitions({
|
||||
const servers = await loadServerDefinitions({
|
||||
configPath,
|
||||
rootDir: tmpDir,
|
||||
});
|
||||
|
||||
const server = servers[0];
|
||||
if (!server) {
|
||||
throw new Error('expected server definition');
|
||||
}
|
||||
expect(server.command.kind).toBe('stdio');
|
||||
if (server.command.kind !== 'stdio') {
|
||||
throw new Error('expected stdio command');
|
||||
|
||||
@ -6,7 +6,7 @@ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/
|
||||
import express from 'express';
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||
import { z } from 'zod';
|
||||
import { __test as generateCliInternals, generateCli } from '../src/generate-cli.js';
|
||||
import { generateCli, __test as generateCliInternals } from '../src/generate-cli.js';
|
||||
|
||||
let baseUrl: URL;
|
||||
const tmpDir = path.join(process.cwd(), 'tmp', 'mcporter-cli-tests');
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import type { Runtime, ServerToolInfo } from '../src/runtime.js';
|
||||
import type { CallResult } from '../src/index.js';
|
||||
import { createServerProxy } from '../src/index.js';
|
||||
import type { Runtime, ServerToolInfo } from '../src/runtime.js';
|
||||
|
||||
type CallLogEntry = {
|
||||
server: string;
|
||||
@ -55,7 +55,10 @@ function createComposableRuntime() {
|
||||
callLog.push({ server, tool: toolName, options });
|
||||
|
||||
if (server === 'docs' && toolName === 'lookup') {
|
||||
const query = typeof options?.args === 'object' && options?.args !== null ? (options.args as { query?: string }).query : undefined;
|
||||
const query =
|
||||
typeof options?.args === 'object' && options?.args !== null
|
||||
? (options.args as { query?: string }).query
|
||||
: undefined;
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
|
||||
@ -233,7 +233,9 @@ describe('stdio transport environment', () => {
|
||||
cwd: '/repo',
|
||||
},
|
||||
env: {
|
||||
// biome-ignore lint/suspicious/noTemplateCurlyInString: placeholders resolve against process env at runtime
|
||||
OBSIDIAN_API_KEY: '${OBSIDIAN_API_KEY}',
|
||||
// biome-ignore lint/suspicious/noTemplateCurlyInString: placeholders resolve against process env at runtime
|
||||
OBSIDIAN_BASE_URL: '${OBSIDIAN_BASE_URL:-https://127.0.0.1:27124}',
|
||||
EMPTY_VAR: '',
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user