brightspace-mcp-server 3.7.12 → 3.7.14

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/README.md CHANGED
@@ -19,7 +19,7 @@ Connects to D2L Brightspace. Automatic login supports Purdue's Microsoft Entra f
19
19
 
20
20
  ## Install
21
21
 
22
- **You need:** [Node.js 20+](https://nodejs.org/) and an available native credential store: macOS Keychain, Windows Credential Manager, or Linux Secret Service. Linux requires `secret-tool` and an unlocked desktop keyring. Install `libsecret-tools` on Debian/Ubuntu, or the package providing `secret-tool` on your distribution. A container or SSH session without Secret Service cannot persist authentication in v2.
22
+ **You need:** [Node.js 20+](https://nodejs.org/) and a native credential store: macOS Keychain, Windows Credential Manager, or Linux Secret Service (requires `secret-tool` and an unlocked keyring — install `libsecret-tools` on Debian/Ubuntu, or your distribution's `secret-tool` package). A container or SSH session without Secret Service can't persist authentication in v2.
23
23
 
24
24
  **Option 1: Let your AI do it**
25
25
 
@@ -58,7 +58,7 @@ npx -y brightspace-mcp-server@latest setup --tudelft
58
58
 
59
59
  Use your NetID rather than your student email address. The TU Delft flow is headless NetID username and password sign-in only, including automatic re-authentication when the saved session expires; it does not support MFA or any other interactive step. If your account requires one, [open an issue](https://github.com/RohanMuppa/brightspace-mcp-server/issues) — that tenant isn't supported yet.
60
60
 
61
- The wizard saves your password in the native credential store and asks how you complete MFA. Authentication can wait for approval or number matching, prompt in the terminal for a code from Google Authenticator or another app, or open a visible browser for other interactive methods. The wizard can configure Claude Desktop, Cursor, Codex Desktop and CLI, and Claude Code when they are installed. Restart your AI client when it finishes.
61
+ The wizard saves your password in the native credential store and asks how you'll complete MFA: wait for approval or number matching, enter a terminal code from Google Authenticator or another app, or use a visible browser for other interactive methods. It can also configure Claude Desktop, Cursor, Codex Desktop and CLI, and Claude Code when installed — restart your AI client when it finishes.
62
62
 
63
63
  Any other D2L school: run `setup` without a flag and paste your Brightspace URL (for example `https://yourschool.brightspace.com`).
64
64
 
@@ -95,27 +95,19 @@ Claude Desktop uses a separate configuration, which the setup wizard can update
95
95
 
96
96
  ## Session Expired?
97
97
 
98
- There is nothing to log into first. Ask for your grades and the sign-in happens as part of that request, so the assistant never has to check whether you are authenticated before it can answer. Starting your AI client touches Brightspace not at all: a restart on its own will never set off an MFA prompt.
98
+ There's no login step — asking a question signs you in.
99
99
 
100
- Returning the next day normally requires no action. The server renews short-lived API tokens over HTTPS using the saved Brightspace session. If that session ends, a browser restores your saved Microsoft session and tries silent SSO. Approval and code-based modes stay headless; when an automatic run needs a code, run the auth command below to enter it securely in the terminal.
100
+ **Normal days:** tokens renew over HTTPS, and a background browser silently replays your saved Microsoft session (silent SSO) if it lapses.
101
101
 
102
- If visible-browser mode is configured, the auth command opens a window that stays open for up to five minutes so you can finish credentials and MFA manually when automatic sign-in cannot continue. Background recovery during a tool call always runs headless so it never opens a browser over your work; set `D2L_HEADLESS=false` in the server's environment to let it open one too. Rerunning setup preserves your previous hidden or visible choice as the prompt default.
102
+ **When MFA is asked:** the number to approve shows up right in the tool's response, and sign-in finishes in the background — approve it, call the tool again, and use the newest number if one goes stale. TOTP apps (Google Authenticator, etc.) get prompted via the terminal; visible-browser mode opens a window instead, though automatic recovery during a tool call still runs headless unless you set `D2L_HEADLESS=false`. On Duo, sign-in auto-answers "Is this your device?" with **yes**, since a headless run has nobody to click it — this also makes Duo remember the device, so skip it on shared machines. Set `D2L_DUO_PASSCODE` to swap the push for a typed passcode.
103
103
 
104
- Your school's policy controls when MFA is required. There is no local 24-hour cutoff, and the server no longer discards browser state after one hour. A network outage preserves the saved session and returns a temporary error.
105
-
106
- If you miss an MFA request, automatic browser authentication pauses for five minutes before trying again. Existing tokens and HTTP token renewal still work. Browser-based SSO also pauses because Microsoft can send another phone prompt during a redirect, even without a password submission. Run this command in a terminal to retry immediately, see a number match, or enter an authenticator code:
104
+ **If it gets stuck:** a missed MFA approval pauses automatic sign-in for 5 minutes. This retries immediately, takes over a stuck sign-in, or asks for a code:
107
105
 
108
106
  ```bash
109
107
  npx -y brightspace-mcp-server@latest auth
110
108
  ```
111
109
 
112
- Run it from your home folder. On macOS, a terminal that lacks Files and Folders permission (the terminal panel inside Claude Desktop, or a fresh editor terminal) cannot start `npx` from inside Documents, Desktop, or Downloads — it fails with `EPERM: process.cwd failed … uv_cwd` before the server runs. The same applies if your AI client launches the server with one of those folders as its working directory; grant the app access under System Settings → Privacy & Security → Files and Folders, or start the server elsewhere.
113
-
114
- **MFA at Purdue** commonly uses Microsoft Authenticator number matching (some schools use Duo instead). When a sign-in needs it, the tool call itself returns quickly with the number to enter, rather than sitting silent for up to five minutes — approve it on your phone, then call the tool again; sign-in finishes in the background in the meantime. Google Authenticator and other one-time-code apps work too, with no setting to change: run the auth command above in a terminal and it prompts for the code when your provider asks for one. Pick the visible-browser option during setup only if your identity provider needs interaction the server cannot drive. The MCP also sends authentication progress as logging notifications to clients that display them, useful if you don't see the number in the tool response for some reason.
115
-
116
- The number in that response can go stale if the request times out or you tap Deny before approving it: the server asks Microsoft for another one and the next tool call reports the new number, so always enter whatever number the most recent response shows. If a background sign-in gets stuck, running the auth command above in a terminal takes it over immediately instead of waiting for it to finish or time out.
117
-
118
- **On a Duo tenant**, if Duo asks "Is this your device?" before it will send a push, automatic sign-in answers **yes** so the push can go out at all — a headless run has nobody to click it otherwise. That also makes Duo remember the device, which skips its own device check on later logins from this machine. Don't run automatic sign-in on a shared or public computer if you'd rather Duo keep asking. Setting `D2L_DUO_PASSCODE` to any value switches from waiting for a push to typing a code from Duo Mobile's passcode option instead.
110
+ Run it from your home folder — macOS blocks `npx` from Documents, Desktop, or Downloads without Files and Folders permission (`EPERM`). Grant access in System Settings → Privacy & Security → Files and Folders, or run elsewhere.
119
111
 
120
112
  ## What You Can Ask About
121
113
 
@@ -30,6 +30,11 @@ const SILENT_SSO = {
30
30
  kmsiCheckbox: "#KmsiCheckboxField",
31
31
  kmsiTitle: "Stay signed in?",
32
32
  kmsiSubmit: "#idSIButton9",
33
+ // Microsoft's passwordless approval view ("Approve sign in request", or
34
+ // "Request wasn't sent" once a push fails) offers the password instead. The
35
+ // text fallback covers a tenant that renames the id.
36
+ passwordSwitch: "#idA_PWD_SwitchToPassword",
37
+ passwordSwitchText: "Use your password instead",
33
38
  };
34
39
  export class BrowserAuthTransportError extends Error {
35
40
  code = "AUTH_TRANSPORT";
@@ -338,6 +343,7 @@ export class BrowserAuth {
338
343
  const deadline = Date.now() + SILENT_SSO_TIMEOUT_MS;
339
344
  let accountHintAttempted = false;
340
345
  let passwordPromptPolls = 0;
346
+ let passwordSwitchAttempted = false;
341
347
  do {
342
348
  if (await this.hasLiveSession(page))
343
349
  return true;
@@ -359,6 +365,22 @@ export class BrowserAuth {
359
365
  log("INFO", "The identity provider requires a login challenge");
360
366
  return false;
361
367
  }
368
+ const passwordSwitch = await this.passwordSignInOption(page);
369
+ if (passwordSwitch) {
370
+ // The approval view has no field or MFA title this poll recognizes, so
371
+ // without leaving it the poll would wait out its whole budget. Switch
372
+ // once when a password is saved; the password page then settles the
373
+ // poll like any other credential prompt.
374
+ if (!passwordSwitchAttempted && this.ssoFlow.hasCredentials()) {
375
+ passwordSwitchAttempted = true;
376
+ await passwordSwitch.click().catch(() => { });
377
+ log("DEBUG", "Chose the password sign-in option over passwordless approval");
378
+ await page.waitForTimeout(SILENT_SSO_POLL_MS);
379
+ continue;
380
+ }
381
+ log("INFO", "The identity provider requires a password sign-in");
382
+ return false;
383
+ }
362
384
  if (await this.isAnyOnScreen(page, SILENT_SSO.credentialFields)) {
363
385
  passwordPromptPolls += 1;
364
386
  // Entra can briefly expose a password-shaped control while its email
@@ -390,6 +412,13 @@ export class BrowserAuth {
390
412
  return await this.isAnyOnScreen(page, SILENT_SSO.emailFields) ||
391
413
  await this.isAnyOnScreen(page, SILENT_SSO.credentialFields);
392
414
  }
415
+ /** The visible "Use your password instead" control, when Microsoft offers one. */
416
+ async passwordSignInOption(page) {
417
+ if (await this.isOnScreen(page, SILENT_SSO.passwordSwitch))
418
+ return page.locator(SILENT_SSO.passwordSwitch).first();
419
+ const byText = page.getByText(SILENT_SSO.passwordSwitchText).first();
420
+ return await byText.isVisible().catch(() => false) ? byText : null;
421
+ }
393
422
  /** One definition of the MFA pages the shared authentication loop supports. */
394
423
  async hasMfaChallenge(page) {
395
424
  return isDuoPrompt(page) || await this.isAnyOnScreen(page, SILENT_SSO.mfaChallenges);
@@ -174,7 +174,11 @@ export class PurdueSSOFlow {
174
174
  // and then blames a missing password field.
175
175
  const hintAccepted = this.accountHintSubmitted && !await this.anyVisible(page, EMAIL_SELECTORS);
176
176
  this.accountHintSubmitted = false;
177
- if (!hintAccepted) {
177
+ // Microsoft can also know the account without a hint from this login: a
178
+ // remembered account's passwordless approval view, once awaitSilentSSO
179
+ // takes "Use your password instead", leads to a password page with no
180
+ // username field at all.
181
+ if (!hintAccepted && !await this.isPasswordOnlyPage(page)) {
178
182
  const email = signInName(this.config.username, this.config.baseUrl);
179
183
  if (!await this.fillWhenReady(page, EMAIL_SELECTORS, email)) {
180
184
  throw new UnsupportedAuthenticationError("The identity provider's username field did not appear. Automatic sign-in cannot continue.");
@@ -253,6 +257,10 @@ export class PurdueSSOFlow {
253
257
  await page.waitForTimeout(FIELD_POLL_MS);
254
258
  return await this.anyVisible(page, PASSWORD_SELECTORS);
255
259
  }
260
+ /** A stable password field with no username field beside it. */
261
+ async isPasswordOnlyPage(page) {
262
+ return await this.hasCoVisiblePassword(page) && !await this.anyVisible(page, EMAIL_SELECTORS);
263
+ }
256
264
  async hasPostCredentialChallenge(page) {
257
265
  return this.duoMfa.isChallenge(page) || await this.anyVisible(page, [
258
266
  NUMBER_MATCH_SELECTOR,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brightspace-mcp-server",
3
- "version": "3.7.12",
3
+ "version": "3.7.14",
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",