chore: release 0.7.2

This commit is contained in:
Peter Steinberger 2025-12-29 20:32:01 +01:00
parent cd36e8fe61
commit d1cf5572f9
7 changed files with 669 additions and 799 deletions

View File

@ -4,6 +4,18 @@
No unreleased changes yet.
## [0.7.2] - 2025-12-29
### CLI
- Fixed generated CLIs to read Commander.js option values via camelCased properties so snake_case tool schemas map correctly.
### Tests
- Added regression coverage for snake_case, camelCase, and numeric option names in generated CLIs.
- Increased the Bun bundler integration-test timeout to reduce flakes on slower runners.
### Tooling / Dependencies
- Updated dependency set (SDK, Rolldown, Zod, Biome, Oxlint, Bun types).
- Synced the Biome schema URL to the current CLI version.
## [0.7.1] - 2025-12-08
### Daemon
- Track config file mtimes for every loaded layer (home + project or explicit) in daemon metadata and auto-restart when any layer changes, so newly added keep-alive servers are picked up without manual restarts. Includes regression tests for stale-daemon detection.

View File

@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.8/schema.json",
"$schema": "https://biomejs.dev/schemas/2.3.10/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",

View File

@ -1,6 +1,6 @@
{
"name": "mcporter",
"version": "0.7.1",
"version": "0.7.2",
"description": "TypeScript runtime and CLI for connecting to configured Model Context Protocol servers.",
"packageManager": "pnpm@10.22.0",
"pnpm": {
@ -48,30 +48,30 @@
},
"dependencies": {
"@iarna/toml": "^2.2.5",
"@modelcontextprotocol/sdk": "^1.24.3",
"@modelcontextprotocol/sdk": "^1.25.1",
"acorn": "^8.15.0",
"commander": "^14.0.2",
"es-toolkit": "^1.42.0",
"es-toolkit": "^1.43.0",
"jsonc-parser": "^3.3.1",
"ora": "^9.0.0",
"rolldown": "1.0.0-beta.53",
"zod": "^4.1.13"
"rolldown": "1.0.0-beta.57",
"zod": "^4.2.1"
},
"devDependencies": {
"@biomejs/biome": "^2.3.8",
"@biomejs/biome": "^2.3.10",
"@types/estree": "^1.0.8",
"@types/express": "^5.0.6",
"@types/node": "^24.10.1",
"@typescript/native-preview": "7.0.0-dev.20251205.1",
"bun-types": "^1.3.3",
"@types/node": "^25.0.3",
"@typescript/native-preview": "7.0.0-dev.20251229.1",
"bun-types": "^1.3.5",
"cross-env": "^10.1.0",
"express": "^5.2.1",
"oxlint": "^1.31.0",
"oxlint-tsgolint": "^0.8.3",
"oxlint": "^1.36.0",
"oxlint-tsgolint": "^0.10.0",
"rimraf": "^6.1.2",
"tsx": "^4.21.0",
"typescript": "^5.9.3",
"vitest": "^4.0.15"
"vitest": "^4.0.16"
},
"engines": {
"node": ">=20.11.0"

1322
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -434,8 +434,12 @@ export function renderToolCommand(
});
const buildArgs = tool.options
.map((option) => {
// Commander.js converts kebab-case flags to camelCase property names
const camelCaseProp = option.cliName.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
// Commander.js camelcases flag names (e.g. --relative-path => relativePath).
const camelCaseProp = option.cliName
.split('-')
.filter(Boolean)
.map((segment, index) => (index === 0 ? segment : `${segment.charAt(0).toUpperCase()}${segment.slice(1)}`))
.join('');
const source = `cmdOpts.${camelCaseProp}`;
return `if (${source} !== undefined) args.${option.property} = ${source};`;
})

View File

@ -200,7 +200,57 @@ describe('mcporter CLI integration', () => {
expect(helpOutput.stdout).toContain('ping - Simple health check');
expect(helpOutput.stdout).toContain('--echo <echo>');
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {});
});
}, 20000);
it('generates a Bun CLI that can call a tool', async () => {
if (!(await ensureBunSupport('Bun CLI execution test'))) {
return;
}
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mcporter-bun-cli-'));
await fs.writeFile(
path.join(tempDir, 'package.json'),
JSON.stringify({ name: 'mcporter-bun-cli', version: '0.0.0' }, null, 2),
'utf8'
);
const bundlePath = path.join(tempDir, 'context7-bun-cli.js');
await new Promise<void>((resolve, reject) => {
execFile(
process.execPath,
[CLI_ENTRY, 'generate-cli', '--command', baseUrl.toString(), '--runtime', 'bun', '--bundle', bundlePath],
{
cwd: tempDir,
env: { ...process.env, MCPORTER_NO_FORCE_EXIT: '1' },
},
(error) => {
if (error) {
reject(error);
return;
}
resolve();
}
);
});
const result = await new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
execFile(
bundlePath,
['ping', '--echo', 'ban', '--output', 'json'],
{ env: process.env },
(error, stdout, stderr) => {
if (error) {
reject(error);
return;
}
resolve({ stdout, stderr });
}
);
});
const parsed = JSON.parse(result.stdout.trim()) as { ok: boolean; echo?: string };
expect(parsed.ok).toBe(true);
expect(parsed.echo).toBe('ban');
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {});
}, 20000);
it('runs "node dist/cli.js generate-cli --compile" when bun is available', async () => {
if (!(await ensureBunSupport('compile integration test'))) {

View File

@ -54,6 +54,24 @@ if (process.platform !== 'win32') {
};
}
);
server.registerTool(
'option_case_test',
{
title: 'Option Case Test',
description: 'Tool with snake_case, camelCase, and numeric option names',
inputSchema: {
relative_path: z.string(),
api_key: z.string(),
tls_1_3: z.boolean(),
issueId: z.string(),
},
outputSchema: { ok: z.boolean() },
},
async () => ({
content: [{ type: 'text', text: JSON.stringify({ ok: true }) }],
structuredContent: { ok: true },
})
);
server.registerResource(
'greeting',
new ResourceTemplate('greeting://{name}', { list: undefined }),
@ -317,6 +335,36 @@ describeGenerateCli('generateCli', () => {
expect(stdout).not.toContain('<tool> key=value');
});
it('maps CLI options to Commander camelCase properties', async () => {
const inline = JSON.stringify({
name: 'case-options',
description: 'Case options test',
command: baseUrl.toString(),
});
const outputPath = path.join(tmpDir, 'case-options.ts');
await fs.rm(outputPath, { force: true });
const { outputPath: renderedPath } = await generateCli({
serverRef: inline,
outputPath,
runtime: 'node',
timeoutMs: 5_000,
});
const content = await fs.readFile(renderedPath, 'utf8');
expect(content).toContain('args.relative_path');
expect(content).toContain('args.api_key');
expect(content).toContain('args.tls_1_3');
expect(content).toContain('args.issueId');
expect(content).toContain('cmdOpts.relativePath');
expect(content).toContain('cmdOpts.apiKey');
expect(content).toContain('cmdOpts.tls13');
expect(content).toContain('cmdOpts.issueId');
expect(content).not.toContain('cmdOpts.relative_path');
expect(content).not.toContain('cmdOpts.api_key');
expect(content).not.toContain('cmdOpts.tls_1_3');
});
it('accepts both kebab-case and underscore tool names for generated CLIs', async () => {
const deepwikiRef = JSON.stringify({
name: 'deepwiki',