brightspace-mcp-server 5.1.41 → 5.1.43

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.
@@ -13,16 +13,77 @@ import * as path from "node:path";
13
13
  import { writeFileAtomicSync } from "../utils/atomic-write.js";
14
14
  import { classifyRegistration, cliMcpClients, inspectCliMcpClient, isCliAvailable, registerCliMcpClient, serverCommand, } from "../utils/mcp-client-cli.js";
15
15
  const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
16
+ const CLAUDE_DESKTOP_CONFIG = "claude_desktop_config.json";
17
+ const USER_DATA_LEFT_MARKER = ".user-data-left.";
18
+ function listDirs(dir) {
19
+ try {
20
+ return fs.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
21
+ }
22
+ catch {
23
+ return [];
24
+ }
25
+ }
26
+ function mtimeMs(file) {
27
+ try {
28
+ return fs.statSync(file).mtimeMs;
29
+ }
30
+ catch {
31
+ return -1;
32
+ }
33
+ }
34
+ /**
35
+ * Newer Windows builds of Claude Desktop (the Microsoft Store package) keep
36
+ * their data outside the package, in %LOCALAPPDATA%\Claude-Data\<package>\,
37
+ * and no longer read %APPDATA%\Claude. They leave a
38
+ * `.user-data-left.<package>` file behind in %APPDATA%\Claude to say so.
39
+ * Writing to the old folder there registers the server in a file the app
40
+ * never reads, so Claude Code works and chats in the app do not.
41
+ *
42
+ * Returns the folder the app reads, or null when there is no Claude-Data
43
+ * folder (an install that still uses %APPDATA%\Claude). With several package
44
+ * folders, the one the marker names wins, then the one whose config the app
45
+ * wrote most recently (it rewrites the file as preferences change).
46
+ */
47
+ export function findRelocatedClaudeDataDir(home = os.homedir(), env = process.env) {
48
+ const roaming = env.APPDATA || path.join(home, "AppData", "Roaming");
49
+ const local = env.LOCALAPPDATA || path.join(home, "AppData", "Local");
50
+ const root = path.join(local, "Claude-Data");
51
+ const packages = listDirs(root);
52
+ if (packages.length === 0)
53
+ return null;
54
+ let markers = [];
55
+ try {
56
+ markers = fs.readdirSync(path.join(roaming, "Claude"))
57
+ .filter((name) => name.startsWith(USER_DATA_LEFT_MARKER))
58
+ .map((name) => name.slice(USER_DATA_LEFT_MARKER.length).toLowerCase());
59
+ }
60
+ catch {
61
+ // No old folder at all is fine: the app may never have used it.
62
+ }
63
+ const marked = packages.find((name) => markers.includes(name.toLowerCase()));
64
+ if (marked)
65
+ return path.join(root, marked);
66
+ const withConfig = packages
67
+ .map((name) => ({ name, mtime: mtimeMs(path.join(root, name, CLAUDE_DESKTOP_CONFIG)) }))
68
+ .filter((p) => p.mtime >= 0)
69
+ .sort((a, b) => b.mtime - a.mtime);
70
+ if (withConfig.length > 0)
71
+ return path.join(root, withConfig[0].name);
72
+ return packages.length === 1 ? path.join(root, packages[0]) : null;
73
+ }
16
74
  export function getClaudeDesktopConfigPath(platform = os.platform(), home = os.homedir(), env = process.env) {
17
75
  if (platform === "darwin") {
18
- return path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
76
+ return path.join(home, "Library", "Application Support", "Claude", CLAUDE_DESKTOP_CONFIG);
19
77
  }
20
78
  if (platform === "win32") {
79
+ const relocated = findRelocatedClaudeDataDir(home, env);
80
+ if (relocated)
81
+ return path.join(relocated, CLAUDE_DESKTOP_CONFIG);
21
82
  const appdata = env.APPDATA || path.join(home, "AppData", "Roaming");
22
- return path.join(appdata, "Claude", "claude_desktop_config.json");
83
+ return path.join(appdata, "Claude", CLAUDE_DESKTOP_CONFIG);
23
84
  }
24
85
  if (platform === "linux") {
25
- return path.join(home, ".config", "Claude", "claude_desktop_config.json");
86
+ return path.join(home, ".config", "Claude", CLAUDE_DESKTOP_CONFIG);
26
87
  }
27
88
  return null;
28
89
  }
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { ApiError, DEFAULT_CACHE_TTLS } from "../api/index.js";
7
7
  import { GetDropboxSubmissionsSchema } from "./schemas.js";
8
- import { toolResponse, sanitizeError, errorResponse } from "./tool-helpers.js";
8
+ import { toolResponse, sanitizeError, errorResponse, throwIfCredentialRejected } from "./tool-helpers.js";
9
9
  import { log } from "../utils/logger.js";
10
10
  // Per-submitter feedback lookup is one GET each; cap how many run at once so
11
11
  // a large dropbox folder can't fan out hundreds of concurrent requests.
@@ -100,6 +100,7 @@ export function registerGetDropboxSubmissions(server, apiClient) {
100
100
  feedbackMap.set(userId, fb);
101
101
  }
102
102
  catch (error) {
103
+ throwIfCredentialRejected(error);
103
104
  if (!(error instanceof ApiError && error.status === 404)) {
104
105
  feedbackUnread.add(userId);
105
106
  }
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { ApiError } from "../api/index.js";
7
7
  import { GetDropboxUserSubmissionsSchema } from "./schemas.js";
8
- import { toolResponse, sanitizeError, errorResponse } from "./tool-helpers.js";
8
+ import { toolResponse, sanitizeError, errorResponse, throwIfCredentialRejected } from "./tool-helpers.js";
9
9
  import { log } from "../utils/logger.js";
10
10
  /**
11
11
  * Register get_dropbox_user_submissions tool.
@@ -65,6 +65,7 @@ export function registerGetDropboxUserSubmissions(server, apiClient) {
65
65
  feedback = await apiClient.get(feedbackPath);
66
66
  }
67
67
  catch (error) {
68
+ throwIfCredentialRejected(error);
68
69
  if (!(error instanceof ApiError && error.status === 404)) {
69
70
  feedbackUnread = true;
70
71
  log("INFO", "get_dropbox_user_submissions: Feedback could not be read", { courseId, folderId, userId });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brightspace-mcp-server",
3
- "version": "5.1.41",
3
+ "version": "5.1.43",
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",