brightspace-mcp-server 3.10.15 → 3.10.16
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 +1 -1
- package/build/auth/auth-runner.js +27 -30
- package/build/tools/tool-helpers.js +8 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -106,7 +106,7 @@ All optional, set in your AI app's MCP `env` config or your shell. The full list
|
|
|
106
106
|
|
|
107
107
|
## When it asks you to sign in
|
|
108
108
|
|
|
109
|
-
There's no separate login — asking a question signs you in, and it stays signed in on its own most days. When your school wants two-factor again, the number to approve shows up right in the answer; approve it on your phone and
|
|
109
|
+
There's no separate login — asking a question signs you in, and it stays signed in on its own most days. When your school wants two-factor again, the number to approve shows up right in the answer; approve it on your phone and the assistant keeps checking on its own — each check waits up to 45 seconds and the original question completes as soon as the sign-in does, without you having to say you've approved it. If your AI client shows tool progress messages, the number appears while the question is still running instead. If it ever gets stuck, run this in a terminal:
|
|
110
110
|
|
|
111
111
|
```bash
|
|
112
112
|
npx -y brightspace-mcp-server@latest auth
|
|
@@ -21,18 +21,13 @@ import { devActivity } from "../utils/dev-activity.js";
|
|
|
21
21
|
const AUTH_TIMEOUT_MS = 8 * 60 * 1000;
|
|
22
22
|
const KILL_GRACE_MS = 5000;
|
|
23
23
|
/**
|
|
24
|
-
* How long a caller
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
|
|
31
|
-
const JOIN_GRACE_MS = 5000;
|
|
32
|
-
/**
|
|
33
|
-
* How long a caller that can be told the challenge mid-call (see run()'s
|
|
34
|
-
* onChallenge) keeps waiting for the background sign-in after the challenge
|
|
35
|
-
* appears: long enough to find a phone and approve.
|
|
24
|
+
* How long a caller keeps waiting for the background sign-in once the
|
|
25
|
+
* challenge is known: long enough to find a phone and approve. Applies both
|
|
26
|
+
* to a caller told the challenge mid-call (see run()'s onChallenge) and to a
|
|
27
|
+
* retry that joins the background child after an early answer. The retry
|
|
28
|
+
* used to wait only 5 s, so on clients that send no progress token (Claude
|
|
29
|
+
* Desktop) every retry re-answered "enter the number" and the model asked
|
|
30
|
+
* the user to confirm by hand instead of the server polling for the approval.
|
|
36
31
|
*/
|
|
37
32
|
const MFA_POLL_MS = 45000;
|
|
38
33
|
/**
|
|
@@ -182,18 +177,20 @@ export class AuthRunner {
|
|
|
182
177
|
* Authenticate, joining the login this process already started if there is
|
|
183
178
|
* one. Returns true on success and throws a useful error on failure.
|
|
184
179
|
*
|
|
185
|
-
* Without onChallenge,
|
|
186
|
-
* mfaPending error
|
|
187
|
-
*
|
|
188
|
-
* up to MFA_POLL_MS,
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
180
|
+
* Without onChallenge, a NEW sign-in answers the caller at once when an MFA
|
|
181
|
+
* challenge appears (an mfaPending error carrying the number to enter: the
|
|
182
|
+
* user cannot see it any other way), and the next call joins the background
|
|
183
|
+
* sign-in and polls it for up to MFA_POLL_MS, never past CALL_BUDGET_MS from
|
|
184
|
+
* that call. With onChallenge, the caller is handed the challenge mid-call
|
|
185
|
+
* and the first call itself keeps waiting the same way. Either way an
|
|
186
|
+
* approval within the window completes the caller's original request; past
|
|
187
|
+
* it, the caller gets the same mfaPending answer again and is expected to
|
|
188
|
+
* call once more.
|
|
192
189
|
*/
|
|
193
190
|
async run(onChallenge) {
|
|
194
191
|
const startedAt = Date.now();
|
|
195
192
|
try {
|
|
196
|
-
return await this.runOnce();
|
|
193
|
+
return await this.runOnce(startedAt);
|
|
197
194
|
}
|
|
198
195
|
catch (error) {
|
|
199
196
|
const childDone = this.childDone;
|
|
@@ -209,7 +206,7 @@ export class AuthRunner {
|
|
|
209
206
|
return this.awaitBackgroundChild(childDone, windowMs);
|
|
210
207
|
}
|
|
211
208
|
}
|
|
212
|
-
runOnce() {
|
|
209
|
+
runOnce(startedAt) {
|
|
213
210
|
if (this.inFlight) {
|
|
214
211
|
log("DEBUG", "Joining the authentication already in flight");
|
|
215
212
|
return this.inFlight;
|
|
@@ -220,7 +217,7 @@ export class AuthRunner {
|
|
|
220
217
|
// cross-process lock and return "busy".
|
|
221
218
|
if (this.childDone) {
|
|
222
219
|
log("DEBUG", "Joining the background sign-in still running from an earlier call");
|
|
223
|
-
return this.joinBackgroundChild(this.childDone);
|
|
220
|
+
return this.joinBackgroundChild(this.childDone, startedAt);
|
|
224
221
|
}
|
|
225
222
|
// The latch is released by the flow that owns it, as it settles, so a
|
|
226
223
|
// failed login is never replayed: the tool call after a declined MFA
|
|
@@ -237,23 +234,23 @@ export class AuthRunner {
|
|
|
237
234
|
/**
|
|
238
235
|
* Join a background child from an earlier early-answered call instead of
|
|
239
236
|
* spawning a new one. A joiner must not simply await childDone: that
|
|
240
|
-
* blocks for whatever is left of the 5-minute approval window,
|
|
241
|
-
*
|
|
242
|
-
* after reading "call this tool again" and well before actually approving.
|
|
237
|
+
* blocks for whatever is left of the 5-minute approval window, past the
|
|
238
|
+
* client's request timeout.
|
|
243
239
|
*
|
|
244
240
|
* If no challenge has been reported yet (the child hasn't reached MFA),
|
|
245
241
|
* wait for one — or for the child to finish on its own. Once a challenge
|
|
246
|
-
* is known,
|
|
247
|
-
*
|
|
248
|
-
*
|
|
242
|
+
* is known, poll the child for the rest of this call's budget: the user
|
|
243
|
+
* read the number from the previous answer and is approving right now, so
|
|
244
|
+
* this is the call that should carry the result. A still-unapproved
|
|
245
|
+
* sign-in re-answers with the latest challenge and the caller tries again.
|
|
249
246
|
*/
|
|
250
|
-
async joinBackgroundChild(childDone) {
|
|
247
|
+
async joinBackgroundChild(childDone, startedAt) {
|
|
251
248
|
if (!this.pendingChallenge && this.challengeSignal) {
|
|
252
249
|
await Promise.race([childDone.catch(() => { }), this.challengeSignal]);
|
|
253
250
|
}
|
|
254
251
|
if (!this.pendingChallenge)
|
|
255
252
|
return childDone;
|
|
256
|
-
return this.awaitBackgroundChild(childDone,
|
|
253
|
+
return this.awaitBackgroundChild(childDone, Math.max(0, pollWindowMs(Date.now() - startedAt)));
|
|
257
254
|
}
|
|
258
255
|
/**
|
|
259
256
|
* Race a background child already past its MFA challenge against a wait
|
|
@@ -98,8 +98,10 @@ const AUTH_FAILURE_GUIDANCE = {
|
|
|
98
98
|
`Run \`${AUTH_COMMAND}\` in a terminal (from your home folder) to complete it with the number visible.`,
|
|
99
99
|
failed: `The sign-in did not complete. Run \`${AUTH_COMMAND}\` in a terminal (from your home folder) to see why, ` +
|
|
100
100
|
"or `brightspace-setup` if your saved school or username is wrong.",
|
|
101
|
-
mfaPending: "Approve the sign-in request on your phone (Microsoft Authenticator or Duo)
|
|
102
|
-
"then call this tool again
|
|
101
|
+
mfaPending: "Approve the sign-in request on your phone (Microsoft Authenticator or Duo). " +
|
|
102
|
+
"Tell the user that, then call this tool again right away without waiting for them to " +
|
|
103
|
+
"confirm: each call waits up to 45 seconds for the approval and returns the result as soon " +
|
|
104
|
+
"as the sign-in completes. Keep calling until it succeeds or reports a different error.",
|
|
103
105
|
};
|
|
104
106
|
/**
|
|
105
107
|
* mfaPending is the one kind whose guidance is partly dynamic: `numberMatch`
|
|
@@ -110,8 +112,10 @@ const AUTH_FAILURE_GUIDANCE = {
|
|
|
110
112
|
*/
|
|
111
113
|
function authFailureMessage(error) {
|
|
112
114
|
if (error.kind === "mfaPending" && error.numberMatch) {
|
|
113
|
-
return `Open Microsoft Authenticator and enter ${error.numberMatch} within 5 minutes
|
|
114
|
-
"then call this tool again
|
|
115
|
+
return `Open Microsoft Authenticator and enter ${error.numberMatch} within 5 minutes. ` +
|
|
116
|
+
"Tell the user that number, then call this tool again right away without waiting for them " +
|
|
117
|
+
"to confirm: each call waits up to 45 seconds for the approval and returns the result as " +
|
|
118
|
+
"soon as the sign-in completes. Keep calling until it succeeds or reports a different error.";
|
|
115
119
|
}
|
|
116
120
|
return AUTH_FAILURE_GUIDANCE[error.kind];
|
|
117
121
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brightspace-mcp-server",
|
|
3
|
-
"version": "3.10.
|
|
3
|
+
"version": "3.10.16",
|
|
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",
|