brightspace-mcp-server 3.10.8 → 3.10.9

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/index.js CHANGED
@@ -9,6 +9,7 @@
9
9
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
10
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
11
  import { enableStdoutGuard, log } from "./utils/logger.js";
12
+ import { guardServerProcess } from "./utils/process-guard.js";
12
13
  import { loadConfig } from "./utils/config.js";
13
14
  import { TokenManager, AuthRunner } from "./auth/index.js";
14
15
  import { D2LApiClient } from "./api/index.js";
@@ -45,10 +46,9 @@ else {
45
46
  // ── MCP Server (default) ────────────────────────────────────────────
46
47
  // CRITICAL: Enable stdout guard IMMEDIATELY to prevent corruption of stdio transport
47
48
  enableStdoutGuard();
48
- // Unhandled rejection handler
49
- process.on('unhandledRejection', (reason) => {
50
- log('ERROR', 'Unhandled promise rejection', reason);
51
- });
49
+ // A stray exception or a closed pipe must not silently drop the client's
50
+ // transport; see guardServerProcess.
51
+ guardServerProcess();
52
52
  async function main() {
53
53
  try {
54
54
  // Load configuration
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Purdue Brightspace MCP Server
3
+ * Copyright (c) 2026 Rohan Muppa. All rights reserved.
4
+ * Licensed under MIT — see LICENSE file for details.
5
+ */
6
+ import { log } from "./logger.js";
7
+ const RECONNECT_HINT = "Restart or reconnect the Brightspace MCP server in your MCP client to continue " +
8
+ "(for example, toggle it off and on in the client's MCP settings, or restart the client).";
9
+ /**
10
+ * Keep one bad moment from silently killing a stdio MCP server.
11
+ *
12
+ * Each MCP client launches its own server process. When that process dies,
13
+ * the client only ever reports "Transport closed" for every later call, even
14
+ * calls that never touch the network, while another client's process carries
15
+ * on fine. Node's defaults are what kill it: an exception thrown from any
16
+ * event listener, or an EPIPE on stdout with no 'error' listener, ends the
17
+ * process with nothing useful in the client's log.
18
+ *
19
+ * So: a stray exception is logged and the server keeps serving; a stdout pipe
20
+ * the client has already closed ends the process cleanly, since there is no
21
+ * one left to answer; and every other way the process stops says why on
22
+ * stderr, which MCP clients keep as the server's log.
23
+ */
24
+ export function guardServerProcess(proc = process) {
25
+ proc.on("uncaughtException", (error) => {
26
+ log("ERROR", "Uncaught exception; the server is still running", error);
27
+ });
28
+ proc.on("unhandledRejection", (reason) => {
29
+ log("ERROR", "Unhandled promise rejection", reason);
30
+ });
31
+ proc.stdout.on("error", (error) => {
32
+ if (error.code === "EPIPE") {
33
+ log("INFO", "The MCP client closed the connection (stdout pipe closed); shutting down");
34
+ proc.exit(0);
35
+ return;
36
+ }
37
+ log("ERROR", "Writing to the MCP client failed", error);
38
+ });
39
+ proc.stdin.on("end", () => {
40
+ log("INFO", "The MCP client closed stdin; the server will stop");
41
+ });
42
+ proc.on("exit", (code) => {
43
+ if (code === 0)
44
+ return;
45
+ log("ERROR", `Brightspace MCP server exited with code ${code}. ${RECONNECT_HINT}`);
46
+ });
47
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brightspace-mcp-server",
3
- "version": "3.10.8",
3
+ "version": "3.10.9",
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",