brightspace-mcp-server 3.10.16 → 3.11.1

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
@@ -95,7 +95,7 @@ All optional, set in your AI app's MCP `env` config or your shell. The full list
95
95
 
96
96
  | Setting | What it does |
97
97
  |---------|--------------|
98
- | `D2L_REMEMBER_MFA=true` | Tick Microsoft's "Don't ask again" box so later sign-ins skip the second factor (off by default; not for shared computers) |
98
+ | `D2L_REMEMBER_MFA=true` | Tick Microsoft's "Don't ask again" box so later sign-ins skip the second factor (off by default; not for shared computers). `setup` asks this and saves it as `rememberMfa` in `config.json`; the env var overrides it |
99
99
  | `D2L_HEADLESS=false` | Show the browser window during sign-in, for MFA methods that need a click |
100
100
  | `D2L_DUO_PASSCODE=1` | On Duo, type a passcode instead of waiting for a push |
101
101
  | `D2L_SESSION_COOKIE` / `D2L_ACCESS_TOKEN` | Skip the browser entirely with a pasted cookie or token ([how](docs/sign-in.md#no-browser-paste-a-session-cookie-or-token)) |
package/build/setup.js CHANGED
@@ -355,6 +355,8 @@ export function buildConfigToSave(existing, answers) {
355
355
  password: answers.password,
356
356
  headless: answers.headless,
357
357
  };
358
+ if (answers.rememberMfa !== undefined)
359
+ config.rememberMfa = answers.rememberMfa;
358
360
  if (answers.campus)
359
361
  config.campus = answers.campus;
360
362
  return config;
@@ -484,8 +486,11 @@ async function main() {
484
486
  console.log(" 1. I approve on my phone, or I type a code from an authenticator app (recommended)");
485
487
  console.log(" 2. Something else — open a browser window I can use");
486
488
  let savedHeadless;
489
+ let savedRememberMfa;
487
490
  try {
488
- savedHeadless = configStoreExists() ? loadConfigStore().headless : undefined;
491
+ const saved = configStoreExists() ? loadConfigStore() : undefined;
492
+ savedHeadless = saved?.headless;
493
+ savedRememberMfa = saved?.rememberMfa;
489
494
  }
490
495
  catch {
491
496
  // An invalid old config is replaced by the setup values below.
@@ -502,12 +507,26 @@ async function main() {
502
507
  ? " Authentication will run without a browser window."
503
508
  : " A browser window will open when authentication is needed."));
504
509
  console.log("");
510
+ // Opt-in only: a shared computer must never skip MFA unless asked to.
511
+ const defaultRemember = savedRememberMfa === true ? "yes" : "no";
512
+ let rememberAnswer = "";
513
+ while (!/^(y(es)?|no?)$/i.test(rememberAnswer)) {
514
+ rememberAnswer = await ask(rl2, ` Remember this device so later sign-ins can skip the second factor? Not for shared computers. (yes/no) [${defaultRemember}]: `) || defaultRemember;
515
+ if (!/^(y(es)?|no?)$/i.test(rememberAnswer))
516
+ console.log(yellow(" Please enter yes or no."));
517
+ }
518
+ const rememberMfa = /^y/i.test(rememberAnswer);
519
+ console.log(dim(rememberMfa
520
+ ? " Later sign-ins will ask Microsoft not to repeat the second factor on this device."
521
+ : " Every sign-in will ask for the second factor."));
522
+ console.log("");
505
523
  // ── Step 5: Save config ──────────────────────────────────────────
506
524
  const config = buildConfigToSave(readExistingConfig(), {
507
525
  baseUrl,
508
526
  username,
509
527
  password,
510
528
  headless,
529
+ rememberMfa,
511
530
  campus: campus || undefined,
512
531
  });
513
532
  await saveSecureConfig(config);
@@ -112,7 +112,8 @@ export function registerGetVideoTranscript(server, apiClient, fetchImpl = fetch)
112
112
  "Call it with courseId and topicId from get_course_content (typeFilter: 'video' or 'other'), or with videoUrl " +
113
113
  "directly if you already have the link. Returns transcript text with timestamps, plus title and duration when " +
114
114
  "available. Currently supports Kaltura (e.g. BoilerCast, including its Brightspace LTI links) and YouTube; other platforms return a clear message " +
115
- "naming what isn't supported yet. Use offset/maxChars to page through a long transcript. Read only — this " +
115
+ "naming what isn't supported yet. Each call returns at most maxChars characters (up to 100000); to read a whole " +
116
+ "transcript, call again with offset set to the returned nextOffset until truncated is false. Read only — this " +
116
117
  "never marks the video as watched.",
117
118
  inputSchema: GetVideoTranscriptSchema,
118
119
  }, async (args) => {
@@ -125,7 +125,7 @@ export const GetVideoTranscriptSchema = z.object({
125
125
  offset: z.coerce.number().int().min(0).default(0)
126
126
  .describe("Character offset into the transcript to resume from. Pass back nextOffset from a truncated response to fetch the next piece."),
127
127
  maxChars: z.coerce.number().int().positive().max(100000).default(12000)
128
- .describe("Maximum characters of transcript text to return in one call. The response reports whether it was truncated."),
128
+ .describe("Maximum characters of transcript text to return in one call, from 1 to 100000 (default 12000). The response reports whether it was truncated; to read past this limit, page with offset/nextOffset."),
129
129
  });
130
130
  export const GetRosterSchema = z.object({
131
131
  courseId: z.coerce.number().int().positive()
@@ -48,9 +48,11 @@ export async function loadConfig() {
48
48
  ?? store?.headless
49
49
  ?? true;
50
50
  // Opt-in: ask Microsoft to skip the second factor for its "Don't ask again"
51
- // window. Off unless D2L_REMEMBER_MFA=true, so a shared machine never
52
- // remembers MFA without the user choosing it.
53
- const rememberMfa = envBoolean(process.env.D2L_REMEMBER_MFA, "D2L_REMEMBER_MFA") ?? false;
51
+ // window. Off unless the user said yes in setup or set D2L_REMEMBER_MFA=true,
52
+ // so a shared machine never remembers MFA without the user choosing it.
53
+ const rememberMfa = envBoolean(process.env.D2L_REMEMBER_MFA, "D2L_REMEMBER_MFA")
54
+ ?? store?.rememberMfa
55
+ ?? false;
54
56
  // Resolve tokenTtl: env > store > default (3600)
55
57
  const tokenTtl = positiveSeconds(process.env.D2L_TOKEN_TTL, "D2L_TOKEN_TTL")
56
58
  ?? positiveSeconds(store?.tokenTtl, "tokenTtl in config.json")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brightspace-mcp-server",
3
- "version": "3.10.16",
3
+ "version": "3.11.1",
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",