Fix generate-cli bundling and add CI Bun coverage

This commit is contained in:
Peter Steinberger 2025-11-07 15:14:51 +00:00
parent 44b28db644
commit 6683f35871
5 changed files with 63 additions and 3 deletions

View File

@ -18,6 +18,9 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 22
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.1
- run: pnpm install --no-frozen-lockfile
- run: pnpm --version
- run: pnpm check

View File

@ -2,7 +2,8 @@
## [Unreleased]
_Nothing yet._
### Code generation & metadata
- Fixed a regression where `mcporter generate-cli --bundle/--compile` failed in directories without `commander`/`mcporter` installed by aliasing those dependencies to mcporters own install and verifying through a new end-to-end test that `node dist/cli.js generate-cli` runs cleanly from an empty project (fixes #1).
## [0.3.1] - 2025-11-07

View File

@ -22,7 +22,7 @@ Create an `mcporter generate-cli` command that produces a standalone CLI for a s
- If `--server` matches a configured name (via `loadServerDefinitions`), use that server definition.
- Otherwise, if the value looks like a file path, load a Cursor-style JSON definition from disk.
- Otherwise, attempt to parse inline JSON/JSON5.
- When `--command` is a raw HTTP selector or shell command, normalize scheme-less URLs to HTTPS and split stdio commands into `command` + `args` (e.g., `npx -y chrome-devtools-mcp@latest`).
- When `--command` (or the first positional argument) looks like a shell command (contains whitespace), split it into `command` + `args` and treat it as stdio. Otherwise, normalize HTTP selectors (`https://`, `http://`, or `host/path.tool`) so `generate-cli mcp.context7.com/mcp` autoconfigures an HTTP transport.
- Validate that a definition is found; prompt on failure.
3. **Tool Introspection**
- Use `listTools(server, { includeSchema: true })` to inspect MCP tool schemas.

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. Quoting the command as the first positional argument works too, so `npx mcporter generate-cli "npx -y chrome-devtools-mcp@latest"` is equivalent.
- `--command <url|command>` point at an ad-hoc HTTP endpoint (include `https://` or use `host/path.tool`) or a stdio command (anything with spaces, e.g., `"npx -y chrome-devtools-mcp@latest"`). If you omit `--command`, the first positional argument is inspected: whitespace → stdio, otherwise the parser probes for HTTP/HTTPS and falls back to config names.
- `--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

@ -29,6 +29,14 @@ async function ensureDistBuilt(): Promise<void> {
}
}
async function hasBun(): Promise<boolean> {
return await new Promise<boolean>((resolve) => {
execFile('bun', ['--version'], { cwd: process.cwd(), env: process.env }, (error) => {
resolve(!error);
});
});
}
describe('mcporter CLI integration', () => {
let baseUrl: URL;
let shutdown: (() => Promise<void>) | undefined;
@ -117,4 +125,52 @@ describe('mcporter CLI integration', () => {
expect(stats.isFile()).toBe(true);
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {});
});
it('runs "node dist/cli.js generate-cli --compile" when bun is available', async () => {
if (!(await hasBun())) {
console.warn('bun not available on this runner; skipping compile integration test.');
return;
}
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mcporter-cli-compile-'));
await fs.writeFile(
path.join(tempDir, 'package.json'),
JSON.stringify({ name: 'mcporter-compile-e2e', version: '0.0.0' }, null, 2),
'utf8'
);
const binaryPath = path.join(tempDir, 'context7-cli');
await new Promise<void>((resolve, reject) => {
execFile(
process.execPath,
[CLI_ENTRY, 'generate-cli', '--command', baseUrl.toString(), '--compile', binaryPath, '--runtime', 'bun'],
{
cwd: tempDir,
env: { ...process.env, MCPORTER_NO_FORCE_EXIT: '1' },
},
(error) => {
if (error) {
reject(error);
return;
}
resolve();
}
);
});
const stats = await fs.stat(binaryPath);
expect(stats.isFile()).toBe(true);
const { stdout } = await new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
execFile(binaryPath, ['list-tools'], { env: process.env }, (error, stdout, stderr) => {
if (error) {
reject(error);
return;
}
resolve({ stdout, stderr });
});
});
expect(stdout).toContain('ping - Simple health check');
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {});
});
});