Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 48 additions & 8 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,52 @@
import {readFileSync} from "node:fs";
import Server from "./server.js";

if (process.argv.length > 2) {
process.stderr.write("\n");
process.stderr.write("Unexpected arguments: This command does not accept any arguments.\n");
process.stderr.write("Usage: ui5mcp\n");
process.exit(2);
} else {
const server = new Server();
const args = process.argv.slice(2);
const usage = `Usage: ui5mcp

await server.connect();
Starts the UI5 MCP server over stdio.

Options:
-h, --help Show this help message.
-v, --version Show the version.
`;

function getPackageVersion() {
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")) as unknown;
const hasVersion = typeof packageJson === "object" && packageJson !== null && "version" in packageJson;

if (hasVersion && typeof packageJson.version === "string") {
return packageJson.version;
}

throw new Error("Unable to determine package version");
}

function handleMetadataFlags() {
if (args.length === 1 && ["--help", "-h"].includes(args[0])) {
process.stdout.write(usage);
process.exit(0);
return true;
}

if (args.length === 1 && ["--version", "-v"].includes(args[0])) {
process.stdout.write(`${getPackageVersion()}\n`);
process.exit(0);
return true;
}

return false;
}

if (!handleMetadataFlags()) {
if (args.length > 0) {
process.stderr.write("\n");
process.stderr.write("Unexpected arguments: This command does not accept any arguments.\n");
process.stderr.write("Usage: ui5mcp\n");
process.exit(2);
} else {
const server = new Server();

await server.connect();
}
}
40 changes: 39 additions & 1 deletion test/lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ test.beforeEach((t) => {
t.context.sinon = sinonGlobal.createSandbox();

// Store and mock process.argv
t.context.originalArgv = process.argv;
t.context.originalArgv = process.argv.slice();

// Create a mock Server instance with a connect method
const serverInstance = {
Expand Down Expand Up @@ -122,3 +122,41 @@ test.serial("CLI exits with error when arguments are provided", async (t) => {
// Verify Server constructor was not called
t.false(t.context.ServerConstructor.called);
});

test.serial("CLI prints help and exits without starting the server", async (t) => {
const {sinon, exitStub} = t.context;

const stdoutWriteStub = sinon.stub(process.stdout, "write");

process.argv.push("--help");

await esmock("../../src/cli.js", {
"../../src/server.js": {
default: t.context.ServerConstructor,
},
});

t.true(exitStub.calledOnceWith(0));
t.true(stdoutWriteStub.calledOnce);
t.regex(stdoutWriteStub.firstCall.firstArg as string, /Usage: ui5mcp/);
t.regex(stdoutWriteStub.firstCall.firstArg as string, /--version/);
t.false(t.context.ServerConstructor.called);
});

test.serial("CLI prints version and exits without starting the server", async (t) => {
const {sinon, exitStub} = t.context;

const stdoutWriteStub = sinon.stub(process.stdout, "write");

process.argv.push("--version");

await esmock("../../src/cli.js", {
"../../src/server.js": {
default: t.context.ServerConstructor,
},
});

t.true(exitStub.calledOnceWith(0));
t.true(stdoutWriteStub.calledOnceWith("0.2.12\n"));
t.false(t.context.ServerConstructor.called);
});