brightspace-mcp-server 4.0.6 → 4.0.7
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/build/tools/get-my-grades.js +30 -6
- package/package.json +1 -1
|
@@ -22,8 +22,9 @@ const QUIZ_FEEDBACK_NOTE = "The gradebook has no comment for this item, but it i
|
|
|
22
22
|
"browser such as Respondus LockDown Browser. Open feedbackUrl to check; do not report " +
|
|
23
23
|
"this item as having no feedback.";
|
|
24
24
|
/**
|
|
25
|
-
* The course's quizzes, or
|
|
26
|
-
* quiz list only costs the feedback links, never the grades themselves
|
|
25
|
+
* The course's quizzes, or null when the route refuses or fails. A missing
|
|
26
|
+
* quiz list only costs the feedback links, never the grades themselves, but
|
|
27
|
+
* the caller is told so a gradebook link isn't read as "not a quiz" (#278).
|
|
27
28
|
*/
|
|
28
29
|
async function fetchQuizzes(apiClient, courseId) {
|
|
29
30
|
try {
|
|
@@ -34,7 +35,7 @@ async function fetchQuizzes(apiClient, courseId) {
|
|
|
34
35
|
}
|
|
35
36
|
catch (error) {
|
|
36
37
|
log("DEBUG", `get_my_grades: quiz list unavailable for course ${courseId}`, error);
|
|
37
|
-
return
|
|
38
|
+
return null;
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
/**
|
|
@@ -42,7 +43,7 @@ async function fetchQuizzes(apiClient, courseId) {
|
|
|
42
43
|
* rows keep the quiz source URL and note; other rows link to the gradebook.
|
|
43
44
|
*/
|
|
44
45
|
function toGradeItems(gradeValues, quizzes, baseUrl, courseId) {
|
|
45
|
-
const quizByGradeItem = new Map(quizzes
|
|
46
|
+
const quizByGradeItem = new Map((quizzes ?? [])
|
|
46
47
|
.filter((q) => q.GradeItemId != null)
|
|
47
48
|
.map((q) => [String(q.GradeItemId), q.QuizId]));
|
|
48
49
|
return gradeValues.map((gv) => {
|
|
@@ -65,6 +66,11 @@ function toGradeItems(gradeValues, quizzes, baseUrl, courseId) {
|
|
|
65
66
|
};
|
|
66
67
|
});
|
|
67
68
|
}
|
|
69
|
+
function quizzesUnavailableNotice(courseIds) {
|
|
70
|
+
return (`The quiz list for ${courseIds.length} course(s) (${courseIds.join(", ")}) could not be read, so ` +
|
|
71
|
+
"quiz-scored grade items there link to the gradebook and lack the quiz feedback note. Quiz " +
|
|
72
|
+
"feedback may still exist on the quiz's own page; do not report those items as having no feedback.");
|
|
73
|
+
}
|
|
68
74
|
/**
|
|
69
75
|
* Register get_my_grades tool
|
|
70
76
|
*/
|
|
@@ -122,8 +128,17 @@ export function registerGetMyGrades(server, apiClient, config) {
|
|
|
122
128
|
throw error;
|
|
123
129
|
}
|
|
124
130
|
// Map to clean objects
|
|
125
|
-
const
|
|
131
|
+
const quizzes = await quizzesPromise;
|
|
132
|
+
const grades = toGradeItems(gradeValues, quizzes, config.baseUrl, courseId);
|
|
126
133
|
log("INFO", `get_my_grades: Retrieved ${grades.length} grade items for course ${courseId}`);
|
|
134
|
+
if (quizzes === null) {
|
|
135
|
+
return toolResponse({
|
|
136
|
+
courseId,
|
|
137
|
+
grades,
|
|
138
|
+
quizzesUnavailable: true,
|
|
139
|
+
notice: quizzesUnavailableNotice([courseId]),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
127
142
|
return toolResponse({ courseId, grades });
|
|
128
143
|
}
|
|
129
144
|
// All courses case
|
|
@@ -160,6 +175,7 @@ export function registerGetMyGrades(server, apiClient, config) {
|
|
|
160
175
|
courseId: item.OrgUnit.Id,
|
|
161
176
|
courseName: item.OrgUnit.Name,
|
|
162
177
|
grades,
|
|
178
|
+
...(quizzes === null ? { quizzesUnavailable: true } : {}),
|
|
163
179
|
};
|
|
164
180
|
}
|
|
165
181
|
catch (error) {
|
|
@@ -216,7 +232,12 @@ export function registerGetMyGrades(server, apiClient, config) {
|
|
|
216
232
|
.map(({ failed: _failed, ...rest }) => rest);
|
|
217
233
|
const courses = settled
|
|
218
234
|
.filter((c) => !c.authPending && !c.restricted && !c.failed)
|
|
219
|
-
.map(({ courseId, courseName, grades }) => ({
|
|
235
|
+
.map(({ courseId, courseName, grades, quizzesUnavailable }) => ({
|
|
236
|
+
courseId,
|
|
237
|
+
courseName,
|
|
238
|
+
grades,
|
|
239
|
+
...(quizzesUnavailable ? { quizzesUnavailable } : {}),
|
|
240
|
+
}));
|
|
220
241
|
log("INFO", `get_my_grades: Retrieved grades for ${courses.length} courses, ` +
|
|
221
242
|
`${restrictedCourses.length} restricted (out of ${enrollmentItems.length} enrolled` +
|
|
222
243
|
`${pending.length > 0 ? `, ${pending.length} pending sign-in` : ""}` +
|
|
@@ -255,6 +276,9 @@ export function registerGetMyGrades(server, apiClient, config) {
|
|
|
255
276
|
"so they are listed in failedCourses instead of courses. Do not report these courses as " +
|
|
256
277
|
"having no grades: open each gradeUrl, or call get_my_grades again for that courseId.");
|
|
257
278
|
}
|
|
279
|
+
const quizGaps = courses.filter((c) => c.quizzesUnavailable).map((c) => c.courseId);
|
|
280
|
+
if (quizGaps.length > 0)
|
|
281
|
+
notices.push(quizzesUnavailableNotice(quizGaps));
|
|
258
282
|
if (notices.length > 0)
|
|
259
283
|
response.notice = notices.join(" ");
|
|
260
284
|
return toolResponse(response);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brightspace-mcp-server",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.7",
|
|
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",
|