brightspace-mcp-server 5.2.0 → 5.2.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 +15 -1
- package/build/tools/get-course-content.js +22 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ Works with Claude Desktop, Claude Code, Cursor, ChatGPT Desktop, Windsurf, and a
|
|
|
37
37
|
```
|
|
38
38
|
Install brightspace-mcp-server for me by following
|
|
39
39
|
https://github.com/RohanMuppa/brightspace-mcp-server/blob/main/LLMs.md
|
|
40
|
-
(use --purdue at Purdue, --suny at SUNY, --tudelft at TU Delft, --cuny at CUNY, --leiden at Leiden, --mcgill at McGill, --mun at Memorial University, --ngeeann at Ngee Ann Polytechnic, or --javeriana at Javeriana Cali).
|
|
40
|
+
(use --purdue at Purdue, --suny at SUNY, --western at Western University, --tudelft at TU Delft, --cuny at CUNY, --leiden at Leiden, --mcgill at McGill, --mun at Memorial University, --ngeeann at Ngee Ann Polytechnic, or --javeriana at Javeriana Cali).
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
The AI does everything it can by itself, then gives you one terminal command. That command asks for your password and waits for you to approve the sign-in on your phone. That's the only part that needs you. Afterwards the AI checks that it can see your courses.
|
|
@@ -108,6 +108,18 @@ Every file in the course's Content area is saved into folders named after its mo
|
|
|
108
108
|
|
|
109
109
|
**Some folders are refused.** Downloads can't go to your home folder itself, a drive root, a hidden (dot) folder such as `~/.ssh`, `~/Library` (except cloud-drive and iCloud folders), system folders such as `/etc` or `/System`, or on Windows `Windows`, `Program Files`, `ProgramData` and `AppData`. Anything else is fine, including `/tmp`, external drives and `~/Documents`. Choose a folder like `~/Documents/Brightspace`; the refusal names the rule. The same rule applies to `download_file`, `get_syllabus` and every other tool that saves to disk. The person running the server can allow a folder anyway with `D2L_DOWNLOAD_ROOTS` (see [Settings](#settings-you-might-want)). Full rules: [LLMs.md](LLMs.md).
|
|
110
110
|
|
|
111
|
+
## Tools
|
|
112
|
+
|
|
113
|
+
The server registers these 29 tools. Each tool's arguments and response fields are in [LLMs.md](LLMs.md#available-tools).
|
|
114
|
+
|
|
115
|
+
- **Courses and grades:** `get_my_courses`, `get_my_grades`, `get_upcoming_due_dates`, `get_calendar_events`
|
|
116
|
+
- **Assignments and rubrics:** `get_assignments`, `get_assignment_rubric`, `get_assignment_files`, `get_rubrics_for_object`
|
|
117
|
+
- **Announcements and discussions:** `get_announcements`, `get_announcement_files`, `get_discussions`
|
|
118
|
+
- **Course content:** `get_course_content`, `search_course`, `get_syllabus`, `get_video_transcript`, `download_file`, `download_course_files`
|
|
119
|
+
- **People and groups:** `get_roster`, `get_classlist_emails`, `get_my_groups`, `get_available_groups`
|
|
120
|
+
- **Dropbox (instructors and TAs):** `get_dropbox_folders`, `get_dropbox_submissions`, `get_dropbox_user_submissions`, `get_dropbox_feedback`, `download_dropbox_submission_file`
|
|
121
|
+
- **Account and sign-in:** `sign_in`, `sign_out`, `get_server_info`
|
|
122
|
+
|
|
111
123
|
## Prompts
|
|
112
124
|
|
|
113
125
|
Apps with a prompt picker (like Claude Desktop) also offer four one-click prompts:
|
|
@@ -137,6 +149,8 @@ How often your school asks for two-factor is up to the school, not this tool.
|
|
|
137
149
|
|
|
138
150
|
Duo, authenticator codes, visible-browser mode, and running without a browser at all (Docker, WSL, hardware keys) are covered in [docs/sign-in.md](docs/sign-in.md).
|
|
139
151
|
|
|
152
|
+
**What a sign-in message means,** including Microsoft's "having trouble verifying your account" hold and what to do about each one, is in [docs/troubleshooting.md](docs/troubleshooting.md#what-each-sign-in-message-means). Numbers that expire, and signing in on more than one computer, are in [docs/sign-in.md](docs/sign-in.md#when-a-number-expires).
|
|
153
|
+
|
|
140
154
|
## Settings you might want
|
|
141
155
|
|
|
142
156
|
All optional. Set them in your AI app's MCP `env` config or in your shell. The full list is in [LLMs.md](LLMs.md#environment-variables).
|
|
@@ -170,7 +170,9 @@ export async function buildContentTree(apiClient, courseId, modules, progressMap
|
|
|
170
170
|
if (typeFilter !== 'all' && !matchesTypeFilter(item, typeFilter)) {
|
|
171
171
|
continue;
|
|
172
172
|
}
|
|
173
|
-
|
|
173
|
+
// A null map means progress couldn't be read: completion is unknown,
|
|
174
|
+
// not false, so a failed read can't pass for "nothing completed".
|
|
175
|
+
const topicProgress = progressMap?.get(item.Id);
|
|
174
176
|
const topic = {
|
|
175
177
|
type: 'topic',
|
|
176
178
|
topicType,
|
|
@@ -181,7 +183,7 @@ export async function buildContentTree(apiClient, courseId, modules, progressMap
|
|
|
181
183
|
...availabilityFields(availability),
|
|
182
184
|
...(item.DueDate ? { dueDate: item.DueDate } : {}),
|
|
183
185
|
lastModified: item.LastModifiedDate ?? null,
|
|
184
|
-
isCompleted: topicProgress?.IsRead ?? false,
|
|
186
|
+
isCompleted: progressMap === null ? null : topicProgress?.IsRead ?? false,
|
|
185
187
|
...(topicProgress?.DateCompleted ? { completedDate: topicProgress.DateCompleted } : {}),
|
|
186
188
|
};
|
|
187
189
|
// Add type-specific content
|
|
@@ -307,22 +309,25 @@ export function registerGetCourseContent(server, apiClient) {
|
|
|
307
309
|
const searchTerm = moduleTitle.toLowerCase();
|
|
308
310
|
rootModules = rootModules.filter(m => m.Title.toLowerCase().includes(searchTerm));
|
|
309
311
|
}
|
|
310
|
-
// Fetch user progress for the course (graceful degradation)
|
|
311
|
-
|
|
312
|
+
// Fetch user progress for the course (graceful degradation). 403/404
|
|
313
|
+
// means this course has no progress data for the user. Any other
|
|
314
|
+
// failure leaves completion unknown, so the map stays null and every
|
|
315
|
+
// topic reports isCompleted: null rather than false.
|
|
316
|
+
let progressMap = new Map();
|
|
312
317
|
try {
|
|
313
|
-
progressArray = await apiClient.get(apiClient.le(courseId, '/content/userprogress/'), { ttl: DEFAULT_CACHE_TTLS.courseContent });
|
|
318
|
+
const progressArray = await apiClient.get(apiClient.le(courseId, '/content/userprogress/'), { ttl: DEFAULT_CACHE_TTLS.courseContent });
|
|
319
|
+
for (const p of Array.isArray(progressArray) ? progressArray : []) {
|
|
320
|
+
progressMap.set(p.ContentObjectId, p);
|
|
321
|
+
}
|
|
314
322
|
}
|
|
315
323
|
catch (error) {
|
|
316
|
-
|
|
324
|
+
if (isAuthUnavailable(error))
|
|
325
|
+
throw error;
|
|
317
326
|
if (error?.status !== 404 && error?.status !== 403) {
|
|
318
327
|
log('DEBUG', `Failed to fetch progress for course ${courseId}`, error);
|
|
328
|
+
progressMap = null;
|
|
319
329
|
}
|
|
320
330
|
}
|
|
321
|
-
// Build progress map for O(1) lookups
|
|
322
|
-
const progressMap = new Map();
|
|
323
|
-
for (const p of progressArray) {
|
|
324
|
-
progressMap.set(p.ContentObjectId, p);
|
|
325
|
-
}
|
|
326
331
|
// Recursively build content tree
|
|
327
332
|
let contentTree = await buildContentTree(apiClient, courseId, rootModules, progressMap, typeFilter, maxDepth);
|
|
328
333
|
// Apply modifiedSince filter, if requested, after the tree is built:
|
|
@@ -346,6 +351,12 @@ export function registerGetCourseContent(server, apiClient) {
|
|
|
346
351
|
topicCount,
|
|
347
352
|
moduleCount,
|
|
348
353
|
...(modifiedSince ? { modifiedSince, returned: topicCount, filteredOut } : {}),
|
|
354
|
+
...(progressMap === null
|
|
355
|
+
? {
|
|
356
|
+
progressUnavailable: "Couldn't read this user's completion progress, so every topic has isCompleted null. " +
|
|
357
|
+
"Do not report topics as not completed; try again later.",
|
|
358
|
+
}
|
|
359
|
+
: {}),
|
|
349
360
|
...(unreadable > 0
|
|
350
361
|
? {
|
|
351
362
|
notice: `The contents of ${unreadable} module(s) couldn't be read; they have children null and a ` +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brightspace-mcp-server",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.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",
|