Skip to content

Commit e29d5b7

Browse files
charliecreates[bot]CharlieHelpsmergify[bot]
authored
feat: add proactive update checks (#562)
* feat: add proactive update checks * fix: make update notices manager neutral * fix: restore npm upgrade example * fix: recover version cache rename failures --------- Co-authored-by: CharlieHelps <charlie@charlielabs.ai> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent 3afab6c commit e29d5b7

7 files changed

Lines changed: 645 additions & 1 deletion

File tree

.changeset/lovely-candles-smash.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"hevy-mcp": patch
3+
---
4+
5+
Notify users on stderr about newer major releases or when they are more than
6+
two minor versions behind, using package-manager-neutral guidance.

package-lock.json

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"@sentry/node": "^10.58.0",
7171
"@sentry/opentelemetry": "^10.58.0",
7272
"axios": "^1.18.0",
73+
"semver": "^7.8.5",
7374
"zod": "^4.4.3"
7475
},
7576
"devDependencies": {
@@ -87,6 +88,7 @@
8788
"@kubb/plugin-zod": "^4.38.0",
8889
"@sentry/rollup-plugin": "^5.3.0",
8990
"@types/node": "^26.0.0",
91+
"@types/semver": "^7.7.1",
9092
"@vitest/coverage-v8": "^4.1.9",
9193
"abstract-syntax-tree": "^2.22.0",
9294
"cross-env": "^10.1.0",

src/index.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const testDoubles = vi.hoisted(() => ({
3434
cleanup: vi.fn(),
3535
getShutdownPromise: vi.fn(),
3636
})),
37+
scheduleUpdateCheck: vi.fn(),
3738
sentry: {
3839
init: vi.fn(() => ({})),
3940
setUser: vi.fn(),
@@ -59,6 +60,10 @@ vi.mock("./utils/graceful-shutdown.js", () => ({
5960
installGracefulShutdown: testDoubles.installGracefulShutdown,
6061
}));
6162

63+
vi.mock("./utils/version-check.js", () => ({
64+
scheduleUpdateCheck: testDoubles.scheduleUpdateCheck,
65+
}));
66+
6267
vi.mock("./utils/telemetry.js", () => ({
6368
Sentry: testDoubles.sentry,
6469
tracer: {
@@ -303,6 +308,7 @@ describe("Server entry", () => {
303308
expect(createClient).not.toHaveBeenCalled();
304309
expect(testDoubles.startActiveSpan).not.toHaveBeenCalled();
305310
expect(testDoubles.installGracefulShutdown).not.toHaveBeenCalled();
311+
expect(testDoubles.scheduleUpdateCheck).not.toHaveBeenCalled();
306312

307313
const anyStdioModule = stdioModule as { __transports?: unknown[] };
308314
expect(anyStdioModule.__transports).toHaveLength(0);
@@ -337,6 +343,7 @@ describe("Server entry", () => {
337343
expect(createClient).not.toHaveBeenCalled();
338344
expect(testDoubles.startActiveSpan).not.toHaveBeenCalled();
339345
expect(testDoubles.installGracefulShutdown).not.toHaveBeenCalled();
346+
expect(testDoubles.scheduleUpdateCheck).not.toHaveBeenCalled();
340347

341348
const anyStdioModule = stdioModule as { __transports?: unknown[] };
342349
expect(anyStdioModule.__transports).toHaveLength(0);
@@ -377,6 +384,29 @@ describe("Server entry", () => {
377384
expect(testDoubles.connect.mock.invocationCallOrder[0]).toBeLessThan(
378385
testDoubles.installGracefulShutdown.mock.invocationCallOrder[0] ?? 0,
379386
);
387+
expect(testDoubles.scheduleUpdateCheck).toHaveBeenCalledWith({
388+
packageName: "hevy-mcp",
389+
currentVersion: "dev",
390+
});
391+
});
392+
393+
it("schedules the update check only after connection succeeds", async () => {
394+
process.env = {
395+
...originalEnv,
396+
HEVY_API_KEY: "test-api-key",
397+
};
398+
process.argv = originalArgv.slice(0, 2);
399+
const events: string[] = [];
400+
testDoubles.connect.mockImplementationOnce(async () => {
401+
events.push("connected");
402+
});
403+
testDoubles.scheduleUpdateCheck.mockImplementationOnce(() => {
404+
events.push("scheduled");
405+
});
406+
407+
await runServer();
408+
409+
expect(events).toEqual(["connected", "scheduled"]);
380410
});
381411

382412
it("prefers CLI --hevy-api-key argument over environment variable", async () => {
@@ -414,6 +444,7 @@ describe("Server entry", () => {
414444
await expect(runServer()).rejects.toThrow("connect failed");
415445
expect(testDoubles.connect).toHaveBeenCalled();
416446
expect(testDoubles.installGracefulShutdown).not.toHaveBeenCalled();
447+
expect(testDoubles.scheduleUpdateCheck).not.toHaveBeenCalled();
417448
expect(testDoubles.span.setStatus).toHaveBeenCalledWith({ code: 2 });
418449
});
419450

src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { installGracefulShutdown } from "./utils/graceful-shutdown.js";
2727
import { createClient } from "./utils/hevyClient.js";
2828
import { createMcpClientLogger } from "./utils/mcp-client-logger.js";
2929
import { createInstrumentedStdioTransport } from "./utils/stdio-observability.js";
30+
import { scheduleUpdateCheck } from "./utils/version-check.js";
3031

3132
const name = serviceName;
3233
const version = serviceVersion;
@@ -308,6 +309,10 @@ export async function runServer() {
308309
}
309310
},
310311
);
312+
scheduleUpdateCheck({
313+
packageName: serviceName,
314+
currentVersion: serviceVersion,
315+
});
311316
installGracefulShutdown({ target: server });
312317

313318
span.setStatus({ code: SpanStatusCode.OK });

0 commit comments

Comments
 (0)