brightspace-d2l-mcp 1.3.0 → 1.4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.4.0
4
+
5
+ - Update check. Running a command like `npx brightspace-d2l-mcp setup` on an older version now says which version is out and asks "Update now? (Y/n)". Yes switches to the new version and carries on with the same command. It never asks when an AI app runs the server, in CI, with `--yes`, or when `BRIGHTSPACE_NO_UPDATE_CHECK` is set.
6
+ - Running `npx brightspace-d2l-mcp` with no command in a terminal now shows help instead of silently starting the server. AI apps are unaffected.
7
+
3
8
  ## 1.3.0
4
9
 
5
10
  - Setup closes its window when it finishes on Windows, after a 10 second countdown. Press Enter to close it straight away, type k to keep it open, or pass `--keep-open`. Every command now exits as soon as it is done, so the terminal is never left hanging.
package/README.md CHANGED
@@ -152,6 +152,8 @@ https://github.com/AnonymousOnyx22/brightspace-d2l-mcp/blob/main/llms.txt
152
152
  | `npx brightspace-d2l-mcp status` | Show your school, whether you're signed in, and connected apps |
153
153
  | `npx brightspace-d2l-mcp doctor` | Check Node, your school, sign-in and connected apps, and say how to fix anything wrong |
154
154
  | `npx brightspace-d2l-mcp logout` | Delete the saved session |
155
+
156
+ When a newer version is out, commands ask whether to update first. Set `BRIGHTSPACE_NO_UPDATE_CHECK=1` to turn that off.
155
157
  | `npx brightspace-d2l-mcp uninstall` | Remove from all AI apps and delete all local data |
156
158
  | `npx brightspace-d2l-mcp serve --http` | Run over HTTP for ChatGPT and HTTP-based agents |
157
159
  | `npx brightspace-d2l-mcp config` | Print the MCP config JSON for any other app |
@@ -0,0 +1,76 @@
1
+ import { spawn } from "node:child_process";
2
+ import { homedir } from "node:os";
3
+ import { PACKAGE_NAME } from "../utils/paths.js";
4
+ import { packageVersion } from "../utils/version.js";
5
+ import { isNewerVersion } from "./doctor.js";
6
+ import * as ui from "./ui.js";
7
+ const CHECK_TIMEOUT_MS = 1500;
8
+ /** Set on the relaunched process so it never asks again. */
9
+ const RELAUNCHED_ENV = "BRIGHTSPACE_UPDATED";
10
+ /** Commands that never prompt: the MCP server itself, and output meant for scripts. */
11
+ const SKIP_COMMANDS = new Set(["serve", "config", "--version", "-v"]);
12
+ export function shouldCheckForUpdate(command, args, env = process.env, tty = process.stdin.isTTY === true && process.stdout.isTTY === true) {
13
+ if (!tty || SKIP_COMMANDS.has(command))
14
+ return false;
15
+ if (args.includes("--yes") || args.includes("-y"))
16
+ return false;
17
+ return !env.CI && !env.BRIGHTSPACE_NO_UPDATE_CHECK && !env[RELAUNCHED_ENV];
18
+ }
19
+ async function latestVersion() {
20
+ try {
21
+ const response = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, { signal: AbortSignal.timeout(CHECK_TIMEOUT_MS) });
22
+ if (!response.ok)
23
+ return null;
24
+ const { version } = (await response.json());
25
+ return typeof version === "string" ? version : null;
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ function quoteForCmd(arg) {
32
+ return /[\s"&|<>^]/.test(arg) ? `"${arg.replace(/"/g, '\\"')}"` : arg;
33
+ }
34
+ function relaunch(version, command, args) {
35
+ const windows = process.platform === "win32";
36
+ const argv = ["-y", `${PACKAGE_NAME}@${version}`, command, ...args];
37
+ return new Promise((resolve) => {
38
+ const child = spawn("npx", windows ? argv.map(quoteForCmd) : argv, {
39
+ stdio: "inherit",
40
+ shell: windows,
41
+ // Run outside the current folder, so a project that itself depends on this package can't shadow the new version.
42
+ cwd: homedir(),
43
+ env: { ...process.env, [RELAUNCHED_ENV]: "1" },
44
+ });
45
+ child.on("error", () => resolve(-1));
46
+ child.on("exit", (code) => resolve(code ?? 1));
47
+ });
48
+ }
49
+ /**
50
+ * Before an interactive command, offers to switch to a newer release.
51
+ * Returns the exit code of the updated run, or null to carry on with this version.
52
+ */
53
+ export async function offerUpdate(command, args) {
54
+ if (!shouldCheckForUpdate(command, args))
55
+ return null;
56
+ const current = packageVersion();
57
+ const latest = await latestVersion();
58
+ if (!latest || !isNewerVersion(latest, current))
59
+ return null;
60
+ console.log(`\n ${ui.yellow("↑")} ${ui.bold(`brightspace-d2l-mcp v${latest}`)} is out ${ui.dim(`(you have v${current})`)}`);
61
+ if (!(await ui.confirm("Update now?"))) {
62
+ ui.info(ui.dim(`Skipped. Run npx -y ${PACKAGE_NAME}@latest ${command} any time to update.`));
63
+ return null;
64
+ }
65
+ ui.info(ui.dim(`Downloading v${latest}...`));
66
+ const code = await relaunch(latest, command, args);
67
+ if (code === -1) {
68
+ ui.warn(`Couldn't start the update. Carrying on with v${current}.`);
69
+ return null;
70
+ }
71
+ if (!process.argv[1]?.includes("_npx")) {
72
+ ui.info(ui.dim(`To update your installed copy too, run: npm install -g ${PACKAGE_NAME}@latest`));
73
+ }
74
+ return code;
75
+ }
76
+ //# sourceMappingURL=update-check.js.map
package/build/index.js CHANGED
@@ -1,9 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  const CLI_COMMANDS = new Set(["setup", "login", "status", "doctor", "logout", "uninstall", "serve", "config", "help", "--help", "-h", "--version", "-v"]);
3
- const command = process.argv[2];
3
+ // AI apps launch the server with piped stdio. A person typing the bare command in a terminal gets help instead of a silent server.
4
+ const command = process.argv[2] ?? (process.stdin.isTTY ? "help" : undefined);
4
5
  if (command && CLI_COMMANDS.has(command)) {
5
6
  const { runCli } = await import("./cli/main.js");
6
- const code = await runCli(command, process.argv.slice(3));
7
+ const { offerUpdate } = await import("./cli/update-check.js");
8
+ const args = process.argv.slice(3);
9
+ const code = (await offerUpdate(command, args)) ?? (await runCli(command, args));
7
10
  // Exit as soon as a command finishes. A browser or timer left behind would otherwise keep the terminal busy.
8
11
  // `serve` only returns once its stdio server is running, so it keeps the process alive.
9
12
  if (command === "serve")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brightspace-d2l-mcp",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "mcpName": "io.github.AnonymousOnyx22/brightspace-d2l-mcp",
5
5
  "description": "MCP server for D2L Brightspace: connect Brightspace to Claude, ChatGPT, Cursor, Copilot and any AI agent. One-command setup at any school with normal browser sign-in (SSO, Duo, MFA). Due dates with real submission status, exam and calendar events, what's new catch-up, grades and target-grade calculator, calendar export, assignments, content, announcements.",
6
6
  "type": "module",