brightspace-mcp-server 4.0.0 → 4.0.2

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.
@@ -27,7 +27,7 @@ export function registerGetMyGroups(server, apiClient) {
27
27
  try {
28
28
  log("DEBUG", "get_my_groups tool called", { args });
29
29
  const { courseId } = GetMyGroupsSchema.parse(args);
30
- const { matches, categoriesUnavailable } = await fetchMyGroupMemberships(apiClient, courseId);
30
+ const { matches, categoriesUnavailable, unreadCategories } = await fetchMyGroupMemberships(apiClient, courseId);
31
31
  if (categoriesUnavailable) {
32
32
  return toolResponse({
33
33
  courseId,
@@ -35,6 +35,18 @@ export function registerGetMyGroups(server, apiClient) {
35
35
  note: "No groups are visible for this course (it may not use groups, or group info isn't accessible).",
36
36
  });
37
37
  }
38
+ const unreadNote = unreadCategories.length === 0 ? undefined
39
+ : `Couldn't read the groups in ${unreadCategories.map((c) => `"${c.Name || c.GroupCategoryId}"`).join(", ")}, ` +
40
+ "so any group the current user has there is not listed.";
41
+ if (unreadNote) {
42
+ log("INFO", "get_my_groups: Some group categories could not be read", {
43
+ courseId,
44
+ categoryIds: unreadCategories.map((c) => c.GroupCategoryId),
45
+ });
46
+ }
47
+ if (matches.length === 0 && unreadNote) {
48
+ return toolResponse({ courseId, groups: [], note: unreadNote });
49
+ }
38
50
  if (matches.length === 0) {
39
51
  log("INFO", `get_my_groups: User is not a member of any group in course ${courseId}`);
40
52
  return toolResponse({
@@ -78,7 +90,7 @@ export function registerGetMyGroups(server, apiClient) {
78
90
  };
79
91
  });
80
92
  log("INFO", `get_my_groups: Found ${groups.length} groups for course ${courseId}`);
81
- return toolResponse({ courseId, groups });
93
+ return toolResponse({ courseId, groups, ...(unreadNote ? { note: unreadNote } : {}) });
82
94
  }
83
95
  catch (error) {
84
96
  return sanitizeError(error);
@@ -20,19 +20,24 @@ export async function fetchMyGroupMemberships(apiClient, courseId, categoryId) {
20
20
  : [{ GroupCategoryId: categoryId, Name: "" }];
21
21
  }
22
22
  catch (error) {
23
- if (inaccessible(error))
24
- return { matches: [], categoriesUnavailable: true };
23
+ if (inaccessible(error)) {
24
+ return { matches: [], categoriesUnavailable: true, unreadCategories: [] };
25
+ }
25
26
  throw error;
26
27
  }
27
28
  const matches = [];
29
+ // Categories whose groups answered 403/404: membership there is unknown, not "none".
30
+ const unreadCategories = [];
28
31
  for (const category of categories) {
29
32
  let groups;
30
33
  try {
31
34
  groups = await apiClient.get(apiClient.lp(`/${courseId}/groupcategories/${category.GroupCategoryId}/groups/`), { ttl: DEFAULT_CACHE_TTLS.roster });
32
35
  }
33
36
  catch (error) {
34
- if (inaccessible(error))
37
+ if (inaccessible(error)) {
38
+ unreadCategories.push(category);
35
39
  continue;
40
+ }
36
41
  throw error;
37
42
  }
38
43
  for (const group of groups) {
@@ -40,5 +45,5 @@ export async function fetchMyGroupMemberships(apiClient, courseId, categoryId) {
40
45
  matches.push({ category, group });
41
46
  }
42
47
  }
43
- return { matches, categoriesUnavailable: false };
48
+ return { matches, categoriesUnavailable: false, unreadCategories };
44
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brightspace-mcp-server",
3
- "version": "4.0.0",
3
+ "version": "4.0.2",
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",