Skip to content

Commit b6fe3c0

Browse files
charliecreates[bot]CharlieHelpsmergify[bot]
authored
feat: add --help and --version CLI flags (#431)
* feat: add help and version CLI flags * fix: deprecate --hevy-api-key help text --------- Co-authored-by: CharlieHelps <charlie@charlielabs.ai> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent 28cd395 commit b6fe3c0

3 files changed

Lines changed: 108 additions & 1 deletion

File tree

.changeset/slimy-coins-smash.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"hevy-mcp": patch
3+
---
4+
5+
Add CLI `--help`/`-h` and `--version`/`-v` flags that print output and
6+
exit before server startup, with unit test coverage for flag and default
7+
startup behavior.

src/index.test.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,62 @@ describe("Server entry", () => {
170170
});
171171

172172
describe("runServer", () => {
173+
it.each(["--version", "-v"])(
174+
"prints version for %s and exits before server startup",
175+
async (flag) => {
176+
process.env = {
177+
...originalEnv,
178+
HEVY_API_KEY: "test-api-key",
179+
};
180+
process.argv = [...originalArgv.slice(0, 2), flag];
181+
182+
const logSpy = vi
183+
.spyOn(console, "log")
184+
.mockImplementation(() => undefined);
185+
186+
await runServer();
187+
188+
expect(logSpy).toHaveBeenCalledWith("dev");
189+
expect(createClient).not.toHaveBeenCalled();
190+
expect(testDoubles.startActiveSpan).not.toHaveBeenCalled();
191+
192+
const anyStdioModule = stdioModule as { __transports?: unknown[] };
193+
expect(anyStdioModule.__transports).toHaveLength(0);
194+
195+
logSpy.mockRestore();
196+
},
197+
);
198+
199+
it.each(["--help", "-h"])(
200+
"prints help for %s and exits before server startup",
201+
async (flag) => {
202+
process.env = {
203+
...originalEnv,
204+
HEVY_API_KEY: "test-api-key",
205+
};
206+
process.argv = [...originalArgv.slice(0, 2), flag];
207+
208+
const logSpy = vi
209+
.spyOn(console, "log")
210+
.mockImplementation(() => undefined);
211+
212+
await runServer();
213+
214+
expect(logSpy).toHaveBeenCalledTimes(1);
215+
const [helpText] = logSpy.mock.calls[0] ?? [];
216+
expect(helpText).toContain("Usage:");
217+
expect(helpText).toContain("HEVY_API_KEY");
218+
expect(helpText).toContain("Examples:");
219+
expect(createClient).not.toHaveBeenCalled();
220+
expect(testDoubles.startActiveSpan).not.toHaveBeenCalled();
221+
222+
const anyStdioModule = stdioModule as { __transports?: unknown[] };
223+
expect(anyStdioModule.__transports).toHaveLength(0);
224+
225+
logSpy.mockRestore();
226+
},
227+
);
228+
173229
it("uses HEVY_API_KEY from the environment and connects stdio transport", async () => {
174230
process.env = {
175231
...originalEnv,

src/index.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,38 @@ import { createInstrumentedStdioTransport } from "./utils/stdio-observability.js
2727
const name = serviceName;
2828
const version = serviceVersion;
2929

30+
const HELP_TEXT = [
31+
"Usage:",
32+
" hevy-mcp [options]",
33+
"",
34+
"Options:",
35+
" -h, --help Show this help message and exit",
36+
" -v, --version Show version and exit",
37+
" --hevy-api-key=<api-key> (deprecated, use HEVY_API_KEY env var)",
38+
"",
39+
"Environment:",
40+
" HEVY_API_KEY=<api-key> Hevy API key from Hevy app settings",
41+
"",
42+
"Examples:",
43+
" HEVY_API_KEY=your-key npx hevy-mcp",
44+
" npx hevy-mcp --hevy-api-key=your-key",
45+
" npm start -- --hevy-api-key=your-key",
46+
].join("\n");
47+
48+
function getCliAction(args: string[]): "start" | "version" | "help" {
49+
for (const arg of args) {
50+
if (arg === "--version" || arg === "-v") {
51+
return "version";
52+
}
53+
54+
if (arg === "--help" || arg === "-h") {
55+
return "help";
56+
}
57+
}
58+
59+
return "start";
60+
}
61+
3062
const HEVY_API_BASEURL = "https://api.hevyapp.com";
3163

3264
const SENTRY_USER_ID_CONTEXT = "hevy-mcp:sentry-user-id:v1";
@@ -127,6 +159,19 @@ export function createServer({ config }: { config: ServerConfig }) {
127159
export default createServer;
128160

129161
export async function runServer() {
162+
const args = process.argv.slice(2);
163+
const cliAction = getCliAction(args);
164+
165+
if (cliAction === "version") {
166+
console.log(version);
167+
return;
168+
}
169+
170+
if (cliAction === "help") {
171+
console.log(HELP_TEXT);
172+
return;
173+
}
174+
130175
serverStartups.add(1, { version });
131176

132177
await tracer.startActiveSpan(
@@ -138,7 +183,6 @@ export async function runServer() {
138183
},
139184
async (span) => {
140185
try {
141-
const args = process.argv.slice(2);
142186
const cfg = parseConfig(args, process.env);
143187
const apiKey = cfg.apiKey;
144188
assertApiKey(apiKey);

0 commit comments

Comments
 (0)