brightspace-mcp-server 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +33 -52
  2. package/build/api/client.d.ts +32 -7
  3. package/build/api/client.d.ts.map +1 -1
  4. package/build/api/client.js +93 -27
  5. package/build/api/client.js.map +1 -1
  6. package/build/auth/auth-cooldown.d.ts +8 -0
  7. package/build/auth/auth-cooldown.d.ts.map +1 -1
  8. package/build/auth/auth-cooldown.js +15 -2
  9. package/build/auth/auth-cooldown.js.map +1 -1
  10. package/build/auth/auth-lock.d.ts.map +1 -1
  11. package/build/auth/auth-lock.js +51 -3
  12. package/build/auth/auth-lock.js.map +1 -1
  13. package/build/auth/auth-runner.d.ts +15 -4
  14. package/build/auth/auth-runner.d.ts.map +1 -1
  15. package/build/auth/auth-runner.js +128 -109
  16. package/build/auth/auth-runner.js.map +1 -1
  17. package/build/auth/browser-auth.d.ts +2 -1
  18. package/build/auth/browser-auth.d.ts.map +1 -1
  19. package/build/auth/browser-auth.js +15 -4
  20. package/build/auth/browser-auth.js.map +1 -1
  21. package/build/auth/purdue-sso.d.ts +7 -0
  22. package/build/auth/purdue-sso.d.ts.map +1 -1
  23. package/build/auth/purdue-sso.js +46 -0
  24. package/build/auth/purdue-sso.js.map +1 -1
  25. package/build/auth/sso-flow.d.ts +2 -1
  26. package/build/auth/sso-flow.d.ts.map +1 -1
  27. package/build/auth/sso-flow.js +10 -3
  28. package/build/auth/sso-flow.js.map +1 -1
  29. package/build/auth/suny-sso.d.ts +3 -0
  30. package/build/auth/suny-sso.d.ts.map +1 -1
  31. package/build/auth/suny-sso.js +2 -0
  32. package/build/auth/suny-sso.js.map +1 -1
  33. package/build/auth-cli.js +49 -4
  34. package/build/auth-cli.js.map +1 -1
  35. package/build/index.js +13 -61
  36. package/build/index.js.map +1 -1
  37. package/build/setup.js +32 -13
  38. package/build/setup.js.map +1 -1
  39. package/build/tools/tool-helpers.d.ts +7 -1
  40. package/build/tools/tool-helpers.d.ts.map +1 -1
  41. package/build/tools/tool-helpers.js +96 -11
  42. package/build/tools/tool-helpers.js.map +1 -1
  43. package/build/utils/browser-install.d.ts +23 -0
  44. package/build/utils/browser-install.d.ts.map +1 -0
  45. package/build/utils/browser-install.js +33 -0
  46. package/build/utils/browser-install.js.map +1 -0
  47. package/build/utils/commands.d.ts +26 -0
  48. package/build/utils/commands.d.ts.map +1 -0
  49. package/build/utils/commands.js +26 -0
  50. package/build/utils/commands.js.map +1 -0
  51. package/build/utils/config.d.ts.map +1 -1
  52. package/build/utils/config.js +4 -2
  53. package/build/utils/config.js.map +1 -1
  54. package/build/utils/download-errors.d.ts +45 -0
  55. package/build/utils/download-errors.d.ts.map +1 -0
  56. package/build/utils/download-errors.js +45 -0
  57. package/build/utils/download-errors.js.map +1 -0
  58. package/build/utils/download-helpers.d.ts.map +1 -1
  59. package/build/utils/download-helpers.js +2 -1
  60. package/build/utils/download-helpers.js.map +1 -1
  61. package/build/utils/file-validator.d.ts +1 -1
  62. package/build/utils/file-validator.d.ts.map +1 -1
  63. package/build/utils/file-validator.js +47 -7
  64. package/build/utils/file-validator.js.map +1 -1
  65. package/build/utils/install-sites.d.ts +61 -0
  66. package/build/utils/install-sites.d.ts.map +1 -0
  67. package/build/utils/install-sites.js +207 -0
  68. package/build/utils/install-sites.js.map +1 -0
  69. package/build/utils/self-update.d.ts +61 -0
  70. package/build/utils/self-update.d.ts.map +1 -0
  71. package/build/utils/self-update.js +92 -0
  72. package/build/utils/self-update.js.map +1 -0
  73. package/build/utils/update-checker.d.ts +85 -7
  74. package/build/utils/update-checker.d.ts.map +1 -1
  75. package/build/utils/update-checker.js +141 -36
  76. package/build/utils/update-checker.js.map +1 -1
  77. package/package.json +1 -1
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Brightspace MCP Server. Copyright (c) 2026 Rohan Muppa. MIT licensed.
3
+ *
4
+ * Self-healing for the auth CLI.
5
+ *
6
+ * The MCP server is launched as `npx -y brightspace-mcp-server@latest`, so it
7
+ * refetches on every client restart and is effectively always current. The
8
+ * auth CLI has no such guarantee: a copy installed once with `npm install -g`
9
+ * stays at that version forever. That is how a machine ended up running a
10
+ * v2.0.0 server alongside a v1.2.6 auth CLI whose sign-in flow no longer
11
+ * worked, with nothing to say so.
12
+ *
13
+ * When a stale CLI notices it is behind, it re-runs itself through
14
+ * `npx -y brightspace-mcp-server@latest auth` and hands back that process's
15
+ * exit code. The user types nothing and is not asked anything; the newest code
16
+ * simply runs. Note this does not rewrite the old copy on disk -- it is
17
+ * bypassed, not upgraded. Overwriting it would mean running `npm install -g`
18
+ * from inside a live process, which is the thing this package deliberately
19
+ * does not do.
20
+ *
21
+ * Opt out with D2L_NO_UPDATE_CHECK.
22
+ */
23
+ import { spawn } from "node:child_process";
24
+ import { PACKAGE_NAME } from "./commands.js";
25
+ import { fetchLatestVersion, isNewerVersion, ownNpxCacheDir } from "./update-checker.js";
26
+ /** Set in the child's environment so a re-exec can never recurse. */
27
+ export const REEXEC_SENTINEL = "D2L_REEXECED";
28
+ /**
29
+ * Flags worth carrying into the re-executed process. Only pass through
30
+ * option flags: the subcommand is supplied explicitly, and forwarding stray
31
+ * positional arguments risks changing what the child actually does.
32
+ */
33
+ export function passthroughFlags(argv) {
34
+ return argv.slice(2).filter((arg) => arg.startsWith("-"));
35
+ }
36
+ /**
37
+ * Decide, without doing anything, whether a re-exec is warranted.
38
+ * Split out from the spawn so the policy is testable on its own.
39
+ */
40
+ export function shouldReexec(opts) {
41
+ const { env, latest, installedVersion, runningFromNpxCache } = opts;
42
+ if (env[REEXEC_SENTINEL])
43
+ return false; // we are already the child
44
+ if (env.D2L_NO_UPDATE_CHECK)
45
+ return false;
46
+ if (runningFromNpxCache)
47
+ return false; // npx just fetched this
48
+ if (latest === null)
49
+ return false; // registry unreachable, carry on offline
50
+ return isNewerVersion(latest, installedVersion);
51
+ }
52
+ /**
53
+ * Re-run this command through the latest published release when the running
54
+ * copy is stale.
55
+ *
56
+ * Resolves to the child's exit code when a re-exec happened (the caller should
57
+ * exit with it and do no further work), or null when the current process
58
+ * should simply continue.
59
+ */
60
+ export async function reexecLatestIfStale(deps = {}) {
61
+ const { env = process.env, argv = process.argv, installedVersion, fetchImpl = fetch, runningFromNpxCache = ownNpxCacheDir() !== null, platform = process.platform, spawnImpl = spawn, } = deps;
62
+ if (installedVersion === undefined)
63
+ return null;
64
+ // Cheap local checks first, so an opted-out or npx-launched run never even
65
+ // touches the network.
66
+ if (env[REEXEC_SENTINEL] || env.D2L_NO_UPDATE_CHECK || runningFromNpxCache)
67
+ return null;
68
+ const latest = await fetchLatestVersion(fetchImpl);
69
+ if (!shouldReexec({ env, latest, installedVersion, runningFromNpxCache }))
70
+ return null;
71
+ const args = ["-y", `${PACKAGE_NAME}@latest`, "auth", ...passthroughFlags(argv)];
72
+ console.error(`\nThis copy is v${installedVersion}; v${latest} is available. ` +
73
+ `Running the newer version instead.\n`);
74
+ return await new Promise((resolve) => {
75
+ let child;
76
+ try {
77
+ child = spawnImpl(platform === "win32" ? "npx.cmd" : "npx", args, {
78
+ stdio: "inherit",
79
+ env: { ...env, [REEXEC_SENTINEL]: "1" },
80
+ shell: platform === "win32",
81
+ });
82
+ }
83
+ catch {
84
+ resolve(null); // could not launch npx, fall back to running ourselves
85
+ return;
86
+ }
87
+ // If npx itself cannot start, run the stale copy rather than failing.
88
+ child.on("error", () => resolve(null));
89
+ child.on("exit", (code) => resolve(code ?? 1));
90
+ });
91
+ }
92
+ //# sourceMappingURL=self-update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-update.js","sourceRoot":"","sources":["../../src/utils/self-update.ts"],"names":[],"mappings":"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"}
@@ -1,15 +1,59 @@
1
1
  /**
2
2
  * Background npm update checker: look, and tell, but never touch.
3
3
  *
4
- * On startup the server asks the npm registry for the latest published
5
- * version. If it is newer than the running one, the user is told how to
6
- * update. Nothing is ever installed by this module. The one side effect it
7
- * keeps is scoped to this package's own stale npx cache directories, because
8
- * clearing them is what lets `npx brightspace-mcp-server@latest` actually
9
- * pick up the new version on the next start.
4
+ * The server asks the npm registry for the latest published version, on
5
+ * startup and then periodically, because a stdio MCP server can stay alive for
6
+ * days and a boot-only check would never notice a release. If the registry has
7
+ * something newer, the user is told how to update. Nothing is ever installed
8
+ * by this module.
9
+ *
10
+ * The one side effect it keeps is scoped to this package's own stale npx cache
11
+ * directories, because clearing them is what lets `npx brightspace-mcp-server@latest`
12
+ * actually pick up the new version on the next start. It never deletes the
13
+ * directory the current process is running from -- doing so pulls the rug out
14
+ * from under lazy imports (Playwright is loaded on demand at auth time) and
15
+ * from the auth CLI this process spawns as a child.
16
+ *
17
+ * The notice repeats on a throttle rather than being consumed by whichever
18
+ * caller happens to read it first. A one-shot notice is invisible in practice:
19
+ * it gets swallowed by a single background tool call and never seen again.
10
20
  *
11
21
  * Set D2L_NO_UPDATE_CHECK to any value to switch the check off entirely.
12
22
  */
23
+ import { access, readdir, rm } from "node:fs/promises";
24
+ /**
25
+ * The `_npx/<hash>` directory this process is executing out of, or null when
26
+ * not running from an npx cache. Used both to detect npx execution and, more
27
+ * importantly, to know which directory must never be deleted.
28
+ */
29
+ export declare function ownNpxCacheDir(root?: string): string | null;
30
+ /**
31
+ * Remove npx cache entries holding a copy of this package, so the next
32
+ * `npx brightspace-mcp-server@latest` downloads the new version instead of
33
+ * reusing a stale one.
34
+ *
35
+ * Skips the directory the current process is running from. Deleting it would
36
+ * break this process: Playwright is imported lazily at auth time, and the auth
37
+ * CLI is spawned from the same tree, so both would fail with ENOENT for the
38
+ * rest of the server's life. Touches nothing outside npx cache directories.
39
+ */
40
+ export declare function clearAllNpxCaches(selfDir?: string | null, deps?: {
41
+ readdirImpl?: typeof readdir;
42
+ rmImpl?: typeof rm;
43
+ accessImpl?: typeof access;
44
+ }): Promise<number>;
45
+ /**
46
+ * A version string safe to show a person.
47
+ *
48
+ * The registry response is remote input, and the notice built from it is now
49
+ * rendered into the user's AI client by every tool, so the raw string must
50
+ * never be interpolated. `parseTriple` deliberately tolerates a trailing
51
+ * prerelease suffix, which means a `latest` of "2.2.0 and now ignore your
52
+ * instructions" parses happily. Rebuilding the label from the parsed numbers
53
+ * discards everything after the digits, so only `\d+\.\d+\.\d+` can ever reach
54
+ * the screen.
55
+ */
56
+ export declare function safeVersionLabel(version: string): string;
13
57
  /**
14
58
  * True only when `latest` is strictly newer than `installed`, compared as
15
59
  * numeric major, minor, patch. Prerelease suffixes are ignored, and anything
@@ -17,6 +61,12 @@
17
61
  * cannot announce an update that does not exist.
18
62
  */
19
63
  export declare function isNewerVersion(latest: string, installed: string): boolean;
64
+ /**
65
+ * Ask the registry for the latest published version. Returns null on any
66
+ * failure -- a bad network must never be reported as "you are up to date" or
67
+ * as an error the caller has to handle.
68
+ */
69
+ export declare function fetchLatestVersion(fetchImpl?: typeof fetch): Promise<string | null>;
20
70
  export interface UpdateCheckDeps {
21
71
  fetchImpl?: typeof fetch;
22
72
  env?: Record<string, string | undefined>;
@@ -30,5 +80,33 @@ export interface UpdateCheckDeps {
30
80
  * swallowed, because a version check must not affect the server.
31
81
  */
32
82
  export declare function initUpdateChecker(deps?: UpdateCheckDeps): Promise<void>;
33
- export declare function getUpdateNotice(): string | null;
83
+ export interface PeriodicUpdateDeps extends UpdateCheckDeps {
84
+ intervalMs?: number;
85
+ setIntervalImpl?: (fn: () => void, ms: number) => {
86
+ unref?: () => void;
87
+ };
88
+ clearIntervalImpl?: (handle: unknown) => void;
89
+ }
90
+ /**
91
+ * Check now, then keep checking. Returns a stop function.
92
+ *
93
+ * The interval is unref'd so it can never hold a stdio server open past the
94
+ * point it would otherwise exit.
95
+ */
96
+ export declare function startUpdateChecks(deps?: PeriodicUpdateDeps): () => void;
97
+ /**
98
+ * Read the notice without consuming or throttling it.
99
+ *
100
+ * For one-shot contexts like a CLI, which runs, prints once, and exits.
101
+ */
102
+ export declare function peekUpdateNotice(): string | null;
103
+ /**
104
+ * Read the notice for a repeated context like MCP tool responses, which can
105
+ * fire many times a session. Returns the notice at most once per
106
+ * `minIntervalMs`. `now` is a parameter so throttling is testable without
107
+ * faking timers.
108
+ */
109
+ export declare function getUpdateNotice(now?: number, minIntervalMs?: number): string | null;
110
+ /** Drop any pending notice. Primarily for resetting module state in tests. */
111
+ export declare function clearUpdateNotice(): void;
34
112
  //# sourceMappingURL=update-checker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"update-checker.d.ts","sourceRoot":"","sources":["../../src/utils/update-checker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA8DH;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAQzE;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACrC;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCjF;AAED,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAI/C"}
1
+ {"version":3,"file":"update-checker.d.ts","sourceRoot":"","sources":["../../src/utils/update-checker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAuCvD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,GAAE,MAAoB,GAAG,MAAM,GAAG,IAAI,CAIxE;AAMD;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,GAAE,MAAM,GAAG,IAAuB,EACzC,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,MAAM,CAAA;CAAO,GAC1F,OAAO,CAAC,MAAM,CAAC,CAoBjB;AAQD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAQzE;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,SAAS,GAAE,OAAO,KAAa,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYhG;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACrC;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwCjF;AAED,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK;QAAE,KAAK,CAAC,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IACzE,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/C;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,kBAAuB,GAAG,MAAM,IAAI,CAgB3E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAEhD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,GAAG,GAAE,MAAmB,EACxB,aAAa,GAAE,MAAyB,GACvC,MAAM,GAAG,IAAI,CAKf;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
@@ -1,12 +1,22 @@
1
1
  /**
2
2
  * Background npm update checker: look, and tell, but never touch.
3
3
  *
4
- * On startup the server asks the npm registry for the latest published
5
- * version. If it is newer than the running one, the user is told how to
6
- * update. Nothing is ever installed by this module. The one side effect it
7
- * keeps is scoped to this package's own stale npx cache directories, because
8
- * clearing them is what lets `npx brightspace-mcp-server@latest` actually
9
- * pick up the new version on the next start.
4
+ * The server asks the npm registry for the latest published version, on
5
+ * startup and then periodically, because a stdio MCP server can stay alive for
6
+ * days and a boot-only check would never notice a release. If the registry has
7
+ * something newer, the user is told how to update. Nothing is ever installed
8
+ * by this module.
9
+ *
10
+ * The one side effect it keeps is scoped to this package's own stale npx cache
11
+ * directories, because clearing them is what lets `npx brightspace-mcp-server@latest`
12
+ * actually pick up the new version on the next start. It never deletes the
13
+ * directory the current process is running from -- doing so pulls the rug out
14
+ * from under lazy imports (Playwright is loaded on demand at auth time) and
15
+ * from the auth CLI this process spawns as a child.
16
+ *
17
+ * The notice repeats on a throttle rather than being consumed by whichever
18
+ * caller happens to read it first. A one-shot notice is invisible in practice:
19
+ * it gets swallowed by a single background tool call and never seen again.
10
20
  *
11
21
  * Set D2L_NO_UPDATE_CHECK to any value to switch the check off entirely.
12
22
  */
@@ -15,12 +25,16 @@ import { access, readdir, rm } from "node:fs/promises";
15
25
  import { homedir } from "node:os";
16
26
  import { fileURLToPath } from "node:url";
17
27
  import { dirname, resolve, sep } from "node:path";
18
- const PACKAGE_NAME = "brightspace-mcp-server";
28
+ import { PACKAGE_NAME, GLOBAL_INSTALL_COMMAND, CLEAR_NPX_CACHE_COMMAND, } from "./commands.js";
19
29
  const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
20
30
  const REGISTRY_TIMEOUT_MS = 5000;
31
+ /** How long before the same notice is worth repeating to an MCP client. */
32
+ const NOTICE_REPEAT_MS = 30 * 60 * 1000;
33
+ /** How often a long-lived server re-asks the registry. */
34
+ const DEFAULT_CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000;
21
35
  const __filename = fileURLToPath(import.meta.url);
22
36
  const projectRoot = resolve(dirname(__filename), "..", "..");
23
- let notice = null;
37
+ let state = null;
24
38
  function getInstalledVersion() {
25
39
  try {
26
40
  const pkg = JSON.parse(readFileSync(resolve(projectRoot, "package.json"), "utf-8"));
@@ -30,24 +44,41 @@ function getInstalledVersion() {
30
44
  return "0.0.0";
31
45
  }
32
46
  }
47
+ /**
48
+ * The `_npx/<hash>` directory this process is executing out of, or null when
49
+ * not running from an npx cache. Used both to detect npx execution and, more
50
+ * importantly, to know which directory must never be deleted.
51
+ */
52
+ export function ownNpxCacheDir(root = projectRoot) {
53
+ const normalized = root.split(sep).join("/");
54
+ const match = new RegExp(`^(.*/_npx/[^/]+)/node_modules/${PACKAGE_NAME}`).exec(normalized);
55
+ return match ? resolve(match[1]) : null;
56
+ }
33
57
  function isNpxCache() {
34
- const normalized = projectRoot.split(sep).join("/");
35
- return /[\\/]_npx[\\/][^\\/]+[\\/]node_modules[\\/]brightspace-mcp-server/.test(normalized);
58
+ return ownNpxCacheDir() !== null;
36
59
  }
37
60
  /**
38
- * Remove every npx cache entry that holds a copy of this package, so the next
61
+ * Remove npx cache entries holding a copy of this package, so the next
39
62
  * `npx brightspace-mcp-server@latest` downloads the new version instead of
40
- * reusing a stale one. Touches nothing outside those directories.
63
+ * reusing a stale one.
64
+ *
65
+ * Skips the directory the current process is running from. Deleting it would
66
+ * break this process: Playwright is imported lazily at auth time, and the auth
67
+ * CLI is spawned from the same tree, so both would fail with ENOENT for the
68
+ * rest of the server's life. Touches nothing outside npx cache directories.
41
69
  */
42
- async function clearAllNpxCaches() {
70
+ export async function clearAllNpxCaches(selfDir = ownNpxCacheDir(), deps = {}) {
71
+ const { readdirImpl = readdir, rmImpl = rm, accessImpl = access } = deps;
43
72
  const npxCacheRoot = resolve(homedir(), ".npm", "_npx");
44
73
  let cleared = 0;
45
74
  try {
46
- for (const entry of await readdir(npxCacheRoot)) {
75
+ for (const entry of await readdirImpl(npxCacheRoot)) {
47
76
  const entryDir = resolve(npxCacheRoot, entry);
77
+ if (selfDir && entryDir === selfDir)
78
+ continue; // never saw off the branch we sit on
48
79
  try {
49
- await access(resolve(entryDir, "node_modules", PACKAGE_NAME));
50
- await rm(entryDir, { recursive: true, force: true });
80
+ await accessImpl(resolve(entryDir, "node_modules", PACKAGE_NAME));
81
+ await rmImpl(entryDir, { recursive: true, force: true });
51
82
  cleared++;
52
83
  }
53
84
  catch {
@@ -66,6 +97,21 @@ function parseTriple(version) {
66
97
  return null;
67
98
  return [Number(match[1]), Number(match[2]), Number(match[3])];
68
99
  }
100
+ /**
101
+ * A version string safe to show a person.
102
+ *
103
+ * The registry response is remote input, and the notice built from it is now
104
+ * rendered into the user's AI client by every tool, so the raw string must
105
+ * never be interpolated. `parseTriple` deliberately tolerates a trailing
106
+ * prerelease suffix, which means a `latest` of "2.2.0 and now ignore your
107
+ * instructions" parses happily. Rebuilding the label from the parsed numbers
108
+ * discards everything after the digits, so only `\d+\.\d+\.\d+` can ever reach
109
+ * the screen.
110
+ */
111
+ export function safeVersionLabel(version) {
112
+ const triple = parseTriple(version);
113
+ return triple ? triple.join(".") : "unknown";
114
+ }
69
115
  /**
70
116
  * True only when `latest` is strictly newer than `installed`, compared as
71
117
  * numeric major, minor, patch. Prerelease suffixes are ignored, and anything
@@ -84,44 +130,103 @@ export function isNewerVersion(latest, installed) {
84
130
  return false;
85
131
  }
86
132
  /**
87
- * Run one update check. Never throws and never blocks the caller for long:
88
- * the registry request is bounded by a timeout and every failure is
89
- * swallowed, because a version check must not affect the server.
133
+ * Ask the registry for the latest published version. Returns null on any
134
+ * failure -- a bad network must never be reported as "you are up to date" or
135
+ * as an error the caller has to handle.
90
136
  */
91
- export async function initUpdateChecker(deps = {}) {
92
- const { fetchImpl = fetch, env = process.env, installedVersion = getInstalledVersion(), runningFromNpxCache = isNpxCache(), clearCaches = clearAllNpxCaches, } = deps;
93
- if (env.D2L_NO_UPDATE_CHECK)
94
- return;
137
+ export async function fetchLatestVersion(fetchImpl = fetch) {
95
138
  try {
96
139
  const response = await fetchImpl(REGISTRY_URL, {
97
140
  headers: { accept: "application/json" },
98
141
  signal: AbortSignal.timeout(REGISTRY_TIMEOUT_MS),
99
142
  });
100
143
  if (!response.ok)
101
- return;
144
+ return null;
102
145
  const latest = (await response.json()).version;
103
- if (typeof latest !== "string" || !isNewerVersion(latest, installedVersion))
146
+ return typeof latest === "string" ? latest : null;
147
+ }
148
+ catch {
149
+ return null;
150
+ }
151
+ }
152
+ /**
153
+ * Run one update check. Never throws and never blocks the caller for long:
154
+ * the registry request is bounded by a timeout and every failure is
155
+ * swallowed, because a version check must not affect the server.
156
+ */
157
+ export async function initUpdateChecker(deps = {}) {
158
+ const { fetchImpl = fetch, env = process.env, installedVersion = getInstalledVersion(), runningFromNpxCache = isNpxCache(), clearCaches = () => clearAllNpxCaches(), } = deps;
159
+ if (env.D2L_NO_UPDATE_CHECK)
160
+ return;
161
+ try {
162
+ const latest = await fetchLatestVersion(fetchImpl);
163
+ if (latest === null || !isNewerVersion(latest, installedVersion))
104
164
  return;
165
+ // Both are rendered to the user, so neither goes in raw. `latest` is
166
+ // remote input; `installedVersion` is injectable in tests.
167
+ const from = safeVersionLabel(installedVersion);
168
+ const to = safeVersionLabel(latest);
169
+ const headline = `Update available: v${from} to v${to}.`;
170
+ let text;
105
171
  if (runningFromNpxCache) {
106
172
  const count = await clearCaches();
107
- notice =
108
- `Update available: v${installedVersion} to v${latest}. ` +
109
- `Cleared ${count} stale npx cache director${count === 1 ? "y" : "ies"} for ${PACKAGE_NAME} ` +
110
- `so the next start downloads v${latest}. Restart your MCP client to pick it up.`;
173
+ const cleanup = count > 0
174
+ ? `Cleared ${count} stale npx cache director${count === 1 ? "y" : "ies"} for ${PACKAGE_NAME} ` +
175
+ `(kept the one this server is running from). `
176
+ : "";
177
+ text = `${headline} ${cleanup}Restart your MCP client to pick up v${to}.`;
111
178
  }
112
179
  else {
113
- notice =
114
- `Update available: v${installedVersion} to v${latest}. ` +
115
- `Run: npx ${PACKAGE_NAME}@latest, or npm install -g ${PACKAGE_NAME}@latest`;
180
+ text = `${headline} Run: ${GLOBAL_INSTALL_COMMAND}` +
181
+ `, then ${CLEAR_NPX_CACHE_COMMAND} and restart your MCP client.`;
116
182
  }
183
+ // Preserve lastShownAt when the text is unchanged, so a periodic re-check
184
+ // does not reset the throttle and start repeating the same line.
185
+ state = state?.text === text ? state : { text, lastShownAt: null };
117
186
  }
118
187
  catch {
119
188
  // A version check must never take the server down.
120
189
  }
121
190
  }
122
- export function getUpdateNotice() {
123
- const result = notice;
124
- notice = null;
125
- return result;
191
+ /**
192
+ * Check now, then keep checking. Returns a stop function.
193
+ *
194
+ * The interval is unref'd so it can never hold a stdio server open past the
195
+ * point it would otherwise exit.
196
+ */
197
+ export function startUpdateChecks(deps = {}) {
198
+ const { intervalMs = DEFAULT_CHECK_INTERVAL_MS, setIntervalImpl = setInterval, clearIntervalImpl = clearInterval, ...checkDeps } = deps;
199
+ void initUpdateChecker(checkDeps);
200
+ const handle = setIntervalImpl(() => {
201
+ void initUpdateChecker(checkDeps);
202
+ }, intervalMs);
203
+ handle.unref?.();
204
+ return () => clearIntervalImpl(handle);
205
+ }
206
+ /**
207
+ * Read the notice without consuming or throttling it.
208
+ *
209
+ * For one-shot contexts like a CLI, which runs, prints once, and exits.
210
+ */
211
+ export function peekUpdateNotice() {
212
+ return state?.text ?? null;
213
+ }
214
+ /**
215
+ * Read the notice for a repeated context like MCP tool responses, which can
216
+ * fire many times a session. Returns the notice at most once per
217
+ * `minIntervalMs`. `now` is a parameter so throttling is testable without
218
+ * faking timers.
219
+ */
220
+ export function getUpdateNotice(now = Date.now(), minIntervalMs = NOTICE_REPEAT_MS) {
221
+ if (!state)
222
+ return null;
223
+ if (state.lastShownAt !== null && now - state.lastShownAt < minIntervalMs)
224
+ return null;
225
+ state.lastShownAt = now;
226
+ return state.text;
227
+ }
228
+ /** Drop any pending notice. Primarily for resetting module state in tests. */
229
+ export function clearUpdateNotice() {
230
+ state = null;
126
231
  }
127
232
  //# sourceMappingURL=update-checker.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"update-checker.js","sourceRoot":"","sources":["../../src/utils/update-checker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"update-checker.js","sourceRoot":"","sources":["../../src/utils/update-checker.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brightspace-mcp-server",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "mcpName": "io.github.rohanmuppa/brightspace",
5
5
  "description": "MCP server for Brightspace (D2L). Check grades, due dates, assignments, announcements, syllabus, rosters and more via Claude, ChatGPT, Cursor, Windsurf, or any MCP client.",
6
6
  "type": "module",