canvas-parent-mcp 1.0.3 → 1.1.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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +24 -0
- package/dist/bundle.js +140 -26
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/server.json +2 -2
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "MCP server for Canvas LMS (Instructure) — courses, grades, assignments, announcements, planner, conversations, files",
|
|
10
|
-
"version": "1.
|
|
10
|
+
"version": "1.1.2"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"displayName": "Canvas LMS",
|
|
16
16
|
"source": "./",
|
|
17
17
|
"description": "Canvas LMS (Instructure) MCP server for Claude — student/observer access via natural language",
|
|
18
|
-
"version": "1.
|
|
18
|
+
"version": "1.1.2",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Hall"
|
|
21
21
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "canvas-parent-mcp",
|
|
3
3
|
"displayName": "Canvas LMS",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.1.2",
|
|
5
5
|
"description": "Canvas LMS (Instructure) MCP server for Claude — courses, grades, assignments, announcements, planner, conversations, files",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Chris Hall",
|
package/README.md
CHANGED
|
@@ -108,3 +108,27 @@ This reuses the same SSO + OAuth endpoints the official Canvas Student/Parent ap
|
|
|
108
108
|
## Status
|
|
109
109
|
|
|
110
110
|
Unofficial — not affiliated with Instructure. AI-maintained.
|
|
111
|
+
|
|
112
|
+
## Acknowledgement of Terms
|
|
113
|
+
|
|
114
|
+
By using this MCP server, you acknowledge and agree to the following:
|
|
115
|
+
|
|
116
|
+
**1. This server accesses your own Canvas account via the official Canvas REST API.** Auth happens via your own personal access token, issued by your institution. It does not — and cannot — access anyone else's enrollments, grades, or messages.
|
|
117
|
+
|
|
118
|
+
**2. [Instructure's Canvas API Policy](https://www.instructure.com/policies/canvas-api-policy) governs your use of this server**, in addition to your institution's own acceptable-use policy. The clauses most relevant here:
|
|
119
|
+
|
|
120
|
+
> You may not use our APIs on behalf of any third-party… You may not use or access our APIs for competitive purposes… You may not interfere with our APIs, our systems, or other users… You may not circumvent any contractual usage limits.
|
|
121
|
+
|
|
122
|
+
On rate limits: *"limits are enforced per user access token… with dynamic throttling."* On data: *"Any user information retrieved through the API—including course enrollments, grades, and profile information—should be considered and treated as private information."*
|
|
123
|
+
|
|
124
|
+
You are agreeing to those terms — read by the maintainer 2026-05-23 — every time you invoke a tool in this server.
|
|
125
|
+
|
|
126
|
+
**3. Personal, observer/student/parent use only.** This project is not affiliated with, endorsed by, sponsored by, or in partnership with Instructure, Inc. or any school district. It is a personal automation tool for an authenticated Canvas user (typically a parent observer) to read their own (or their student's) enrollments, assignments, grades, and announcements. Do not use it to bulk-extract a district's course content, redistribute student data, or train AI models on student records.
|
|
127
|
+
|
|
128
|
+
**4. FERPA + your institution's AUP apply.** Student educational records are protected under the federal Family Educational Rights and Privacy Act (FERPA). Even though your token grants you lawful access, **how you store, redistribute, or feed that data into LLMs is regulated**. Treat any output (grades, assignments, comments, conversations) as confidential student data. Your institution's acceptable-use policy may add further restrictions on automated access — check before automating.
|
|
129
|
+
|
|
130
|
+
**5. Your token is yours alone.** **Do not commit `CANVAS_API_TOKEN` to git**, do not paste it in shared chats, and rotate it if it's ever exposed. A leaked token grants full Canvas access scoped to your user.
|
|
131
|
+
|
|
132
|
+
**6. You accept full responsibility** for any consequences of using this server in connection with your Canvas account — rate limiting (dynamic throttling kicks in well below documented limits when Canvas is under load), token revocation, account warnings, institution-admin investigations, or any enforcement action. If Instructure or your institution objects to your use, stop using this server.
|
|
133
|
+
|
|
134
|
+
This section is the maintainer's good-faith summary of the terms — it is not legal advice and does not modify or supersede Instructure's actual Canvas API Policy or your institution's policies.
|
package/dist/bundle.js
CHANGED
|
@@ -7572,6 +7572,10 @@ var require_receiver = __commonJS({
|
|
|
7572
7572
|
* extensions
|
|
7573
7573
|
* @param {Boolean} [options.isServer=false] Specifies whether to operate in
|
|
7574
7574
|
* client or server mode
|
|
7575
|
+
* @param {Number} [options.maxBufferedChunks=0] The maximum number of
|
|
7576
|
+
* buffered data chunks
|
|
7577
|
+
* @param {Number} [options.maxFragments=0] The maximum number of message
|
|
7578
|
+
* fragments
|
|
7575
7579
|
* @param {Number} [options.maxPayload=0] The maximum allowed message length
|
|
7576
7580
|
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
|
7577
7581
|
* not to skip UTF-8 validation for text and close messages
|
|
@@ -7582,6 +7586,8 @@ var require_receiver = __commonJS({
|
|
|
7582
7586
|
this._binaryType = options.binaryType || BINARY_TYPES[0];
|
|
7583
7587
|
this._extensions = options.extensions || {};
|
|
7584
7588
|
this._isServer = !!options.isServer;
|
|
7589
|
+
this._maxBufferedChunks = options.maxBufferedChunks | 0;
|
|
7590
|
+
this._maxFragments = options.maxFragments | 0;
|
|
7585
7591
|
this._maxPayload = options.maxPayload | 0;
|
|
7586
7592
|
this._skipUTF8Validation = !!options.skipUTF8Validation;
|
|
7587
7593
|
this[kWebSocket] = void 0;
|
|
@@ -7611,6 +7617,18 @@ var require_receiver = __commonJS({
|
|
|
7611
7617
|
*/
|
|
7612
7618
|
_write(chunk, encoding, cb) {
|
|
7613
7619
|
if (this._opcode === 8 && this._state == GET_INFO) return cb();
|
|
7620
|
+
if (this._maxBufferedChunks > 0 && this._buffers.length >= this._maxBufferedChunks) {
|
|
7621
|
+
cb(
|
|
7622
|
+
this.createError(
|
|
7623
|
+
RangeError,
|
|
7624
|
+
"Too many buffered chunks",
|
|
7625
|
+
false,
|
|
7626
|
+
1008,
|
|
7627
|
+
"WS_ERR_TOO_MANY_BUFFERED_PARTS"
|
|
7628
|
+
)
|
|
7629
|
+
);
|
|
7630
|
+
return;
|
|
7631
|
+
}
|
|
7614
7632
|
this._bufferedBytes += chunk.length;
|
|
7615
7633
|
this._buffers.push(chunk);
|
|
7616
7634
|
this.startLoop(cb);
|
|
@@ -7940,6 +7958,17 @@ var require_receiver = __commonJS({
|
|
|
7940
7958
|
return;
|
|
7941
7959
|
}
|
|
7942
7960
|
if (data.length) {
|
|
7961
|
+
if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
|
|
7962
|
+
const error51 = this.createError(
|
|
7963
|
+
RangeError,
|
|
7964
|
+
"Too many message fragments",
|
|
7965
|
+
false,
|
|
7966
|
+
1008,
|
|
7967
|
+
"WS_ERR_TOO_MANY_BUFFERED_PARTS"
|
|
7968
|
+
);
|
|
7969
|
+
cb(error51);
|
|
7970
|
+
return;
|
|
7971
|
+
}
|
|
7943
7972
|
this._messageLength = this._totalPayloadLength;
|
|
7944
7973
|
this._fragments.push(data);
|
|
7945
7974
|
}
|
|
@@ -7969,6 +7998,17 @@ var require_receiver = __commonJS({
|
|
|
7969
7998
|
cb(error51);
|
|
7970
7999
|
return;
|
|
7971
8000
|
}
|
|
8001
|
+
if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
|
|
8002
|
+
const error51 = this.createError(
|
|
8003
|
+
RangeError,
|
|
8004
|
+
"Too many message fragments",
|
|
8005
|
+
false,
|
|
8006
|
+
1008,
|
|
8007
|
+
"WS_ERR_TOO_MANY_BUFFERED_PARTS"
|
|
8008
|
+
);
|
|
8009
|
+
cb(error51);
|
|
8010
|
+
return;
|
|
8011
|
+
}
|
|
7972
8012
|
this._fragments.push(buf);
|
|
7973
8013
|
}
|
|
7974
8014
|
this.dataMessage(cb);
|
|
@@ -9175,6 +9215,10 @@ var require_websocket = __commonJS({
|
|
|
9175
9215
|
* multiple times in the same tick
|
|
9176
9216
|
* @param {Function} [options.generateMask] The function used to generate the
|
|
9177
9217
|
* masking key
|
|
9218
|
+
* @param {Number} [options.maxBufferedChunks=0] The maximum number of
|
|
9219
|
+
* buffered data chunks
|
|
9220
|
+
* @param {Number} [options.maxFragments=0] The maximum number of message
|
|
9221
|
+
* fragments
|
|
9178
9222
|
* @param {Number} [options.maxPayload=0] The maximum allowed message size
|
|
9179
9223
|
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
|
9180
9224
|
* not to skip UTF-8 validation for text and close messages
|
|
@@ -9186,6 +9230,8 @@ var require_websocket = __commonJS({
|
|
|
9186
9230
|
binaryType: this.binaryType,
|
|
9187
9231
|
extensions: this._extensions,
|
|
9188
9232
|
isServer: this._isServer,
|
|
9233
|
+
maxBufferedChunks: options.maxBufferedChunks,
|
|
9234
|
+
maxFragments: options.maxFragments,
|
|
9189
9235
|
maxPayload: options.maxPayload,
|
|
9190
9236
|
skipUTF8Validation: options.skipUTF8Validation
|
|
9191
9237
|
});
|
|
@@ -9485,6 +9531,8 @@ var require_websocket = __commonJS({
|
|
|
9485
9531
|
autoPong: true,
|
|
9486
9532
|
closeTimeout: CLOSE_TIMEOUT,
|
|
9487
9533
|
protocolVersion: protocolVersions[1],
|
|
9534
|
+
maxBufferedChunks: 1024 * 1024,
|
|
9535
|
+
maxFragments: 128 * 1024,
|
|
9488
9536
|
maxPayload: 100 * 1024 * 1024,
|
|
9489
9537
|
skipUTF8Validation: false,
|
|
9490
9538
|
perMessageDeflate: true,
|
|
@@ -9727,6 +9775,8 @@ var require_websocket = __commonJS({
|
|
|
9727
9775
|
websocket.setSocket(socket, head, {
|
|
9728
9776
|
allowSynchronousEvents: opts.allowSynchronousEvents,
|
|
9729
9777
|
generateMask: opts.generateMask,
|
|
9778
|
+
maxBufferedChunks: opts.maxBufferedChunks,
|
|
9779
|
+
maxFragments: opts.maxFragments,
|
|
9730
9780
|
maxPayload: opts.maxPayload,
|
|
9731
9781
|
skipUTF8Validation: opts.skipUTF8Validation
|
|
9732
9782
|
});
|
|
@@ -10069,6 +10119,10 @@ var require_websocket_server = __commonJS({
|
|
|
10069
10119
|
* called
|
|
10070
10120
|
* @param {Function} [options.handleProtocols] A hook to handle protocols
|
|
10071
10121
|
* @param {String} [options.host] The hostname where to bind the server
|
|
10122
|
+
* @param {Number} [options.maxBufferedChunks=1048576] The maximum number of
|
|
10123
|
+
* buffered data chunks
|
|
10124
|
+
* @param {Number} [options.maxFragments=131072] The maximum number of message
|
|
10125
|
+
* fragments
|
|
10072
10126
|
* @param {Number} [options.maxPayload=104857600] The maximum allowed message
|
|
10073
10127
|
* size
|
|
10074
10128
|
* @param {Boolean} [options.noServer=false] Enable no server mode
|
|
@@ -10090,6 +10144,8 @@ var require_websocket_server = __commonJS({
|
|
|
10090
10144
|
options = {
|
|
10091
10145
|
allowSynchronousEvents: true,
|
|
10092
10146
|
autoPong: true,
|
|
10147
|
+
maxBufferedChunks: 1024 * 1024,
|
|
10148
|
+
maxFragments: 128 * 1024,
|
|
10093
10149
|
maxPayload: 100 * 1024 * 1024,
|
|
10094
10150
|
skipUTF8Validation: false,
|
|
10095
10151
|
perMessageDeflate: false,
|
|
@@ -10369,6 +10425,8 @@ var require_websocket_server = __commonJS({
|
|
|
10369
10425
|
socket.removeListener("error", socketOnError);
|
|
10370
10426
|
ws.setSocket(socket, head, {
|
|
10371
10427
|
allowSynchronousEvents: this.options.allowSynchronousEvents,
|
|
10428
|
+
maxBufferedChunks: this.options.maxBufferedChunks,
|
|
10429
|
+
maxFragments: this.options.maxFragments,
|
|
10372
10430
|
maxPayload: this.options.maxPayload,
|
|
10373
10431
|
skipUTF8Validation: this.options.skipUTF8Validation
|
|
10374
10432
|
});
|
|
@@ -34495,6 +34553,7 @@ var StdioServerTransport = class {
|
|
|
34495
34553
|
|
|
34496
34554
|
// node_modules/@fetchproxy/protocol/dist/frames.js
|
|
34497
34555
|
var PROTOCOL_VERSION = 2;
|
|
34556
|
+
var HKDF_SESSION_INFO = "fetchproxy/1.0.0/session";
|
|
34498
34557
|
var KNOWN_CAPABILITIES = /* @__PURE__ */ new Set([
|
|
34499
34558
|
"fetch",
|
|
34500
34559
|
"read_cookies",
|
|
@@ -34569,7 +34628,7 @@ var ProtocolError = class extends Error {
|
|
|
34569
34628
|
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
34570
34629
|
var BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
34571
34630
|
var SCOPE_KEY_RE = /^[A-Za-z0-9_.\-]{1,256}$/;
|
|
34572
|
-
var SCOPE_KEY_GLOB_RE = /^[A-Za-z0-9_.\-]{1,
|
|
34631
|
+
var SCOPE_KEY_GLOB_RE = /^[A-Za-z0-9_.\-]{1,256}\*?$/;
|
|
34573
34632
|
var HEADER_NAME_RE = /^[A-Za-z0-9_\-]{1,128}$/;
|
|
34574
34633
|
var HOSTNAME_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$/i;
|
|
34575
34634
|
function assertObject(x, label) {
|
|
@@ -34698,7 +34757,7 @@ function assertStoragePointersArray(value, label, declaredKeys) {
|
|
|
34698
34757
|
if (entry.jsonPointer === void 0) {
|
|
34699
34758
|
throw new ProtocolError(`${label}[${i}].jsonPointer: missing`);
|
|
34700
34759
|
}
|
|
34701
|
-
if (typeof entry.key !== "string" || !
|
|
34760
|
+
if (typeof entry.key !== "string" || !SCOPE_KEY_RE.test(entry.key)) {
|
|
34702
34761
|
throw new ProtocolError(`${label}[${i}].key: invalid key ${JSON.stringify(entry.key)}`);
|
|
34703
34762
|
}
|
|
34704
34763
|
if (typeof entry.jsonPointer !== "string" || !isValidJsonPointer(entry.jsonPointer)) {
|
|
@@ -35480,15 +35539,20 @@ async function startHost(opts) {
|
|
|
35480
35539
|
let extensionWs = null;
|
|
35481
35540
|
const peers = /* @__PURE__ */ new Map();
|
|
35482
35541
|
const ownInnerListeners = [];
|
|
35542
|
+
const disconnectListeners = [];
|
|
35483
35543
|
let ownSession = null;
|
|
35484
35544
|
let resolveOwnSession;
|
|
35485
35545
|
let rejectOwnSession;
|
|
35486
|
-
|
|
35487
|
-
|
|
35488
|
-
|
|
35489
|
-
|
|
35490
|
-
|
|
35491
|
-
|
|
35546
|
+
let ownSessionReady;
|
|
35547
|
+
function resetSessionPromise() {
|
|
35548
|
+
ownSessionReady = new Promise((resolve, reject) => {
|
|
35549
|
+
resolveOwnSession = resolve;
|
|
35550
|
+
rejectOwnSession = reject;
|
|
35551
|
+
});
|
|
35552
|
+
ownSessionReady.catch(() => {
|
|
35553
|
+
});
|
|
35554
|
+
}
|
|
35555
|
+
resetSessionPromise();
|
|
35492
35556
|
let extensionHello = null;
|
|
35493
35557
|
wss.on("connection", (ws) => {
|
|
35494
35558
|
let identified = null;
|
|
@@ -35557,11 +35621,11 @@ async function startHost(opts) {
|
|
|
35557
35621
|
}
|
|
35558
35622
|
const extPub = fromB64(frame.extensionSessionPub);
|
|
35559
35623
|
const shared = await ecdhX25519(opts.ownIdentity.x25519Priv, extPub);
|
|
35560
|
-
const key = await hkdfSha256(shared, ownSessionNonce, enc2.encode(
|
|
35561
|
-
if (
|
|
35562
|
-
|
|
35563
|
-
|
|
35564
|
-
|
|
35624
|
+
const key = await hkdfSha256(shared, ownSessionNonce, enc2.encode(HKDF_SESSION_INFO), 32);
|
|
35625
|
+
if (extensionWs !== ws)
|
|
35626
|
+
return;
|
|
35627
|
+
ownSession = new SessionState(key);
|
|
35628
|
+
resolveOwnSession(ownSession);
|
|
35565
35629
|
} else {
|
|
35566
35630
|
const slot = peers.get(frame.mcpId);
|
|
35567
35631
|
if (slot)
|
|
@@ -35603,6 +35667,9 @@ async function startHost(opts) {
|
|
|
35603
35667
|
if (!ownSession) {
|
|
35604
35668
|
rejectOwnSession(new Error("extension disconnected before ready"));
|
|
35605
35669
|
}
|
|
35670
|
+
ownSession = null;
|
|
35671
|
+
resetSessionPromise();
|
|
35672
|
+
disconnectListeners.forEach((cb) => cb());
|
|
35606
35673
|
}
|
|
35607
35674
|
if (identified === "peer" && peerMcpId)
|
|
35608
35675
|
peers.delete(peerMcpId);
|
|
@@ -35627,6 +35694,9 @@ async function startHost(opts) {
|
|
|
35627
35694
|
},
|
|
35628
35695
|
onOwnInner: (cb) => {
|
|
35629
35696
|
ownInnerListeners.push(cb);
|
|
35697
|
+
},
|
|
35698
|
+
onExtensionDisconnect: (cb) => {
|
|
35699
|
+
disconnectListeners.push(cb);
|
|
35630
35700
|
}
|
|
35631
35701
|
};
|
|
35632
35702
|
}
|
|
@@ -35666,7 +35736,7 @@ async function startPeer(opts) {
|
|
|
35666
35736
|
if (frame.type === "ready" && frame.mcpId === opts.mcpId) {
|
|
35667
35737
|
const extPub = fromB64(frame.extensionSessionPub);
|
|
35668
35738
|
const shared = await ecdhX25519(opts.identity.x25519Priv, extPub);
|
|
35669
|
-
const sessionKey = await hkdfSha256(shared, sessionNonce, enc3.encode(
|
|
35739
|
+
const sessionKey = await hkdfSha256(shared, sessionNonce, enc3.encode(HKDF_SESSION_INFO), 32);
|
|
35670
35740
|
session = new SessionState(sessionKey);
|
|
35671
35741
|
resolve(session);
|
|
35672
35742
|
return;
|
|
@@ -35757,6 +35827,32 @@ async function loadOrCreateIdentity(serverName, dir = defaultIdentityDir()) {
|
|
|
35757
35827
|
return id;
|
|
35758
35828
|
}
|
|
35759
35829
|
|
|
35830
|
+
// node_modules/@fetchproxy/server/dist/error-kind.js
|
|
35831
|
+
function classifyFetchError(error51) {
|
|
35832
|
+
if (/Could not establish connection/i.test(error51) || /Receiving end does not exist/i.test(error51)) {
|
|
35833
|
+
return "content_script_unreachable";
|
|
35834
|
+
}
|
|
35835
|
+
if (/^tab fetch failed:/.test(error51)) {
|
|
35836
|
+
return "tab_fetch_failed";
|
|
35837
|
+
}
|
|
35838
|
+
if (/^fetch threw:/.test(error51)) {
|
|
35839
|
+
return "tab_fetch_failed";
|
|
35840
|
+
}
|
|
35841
|
+
if (/^no tab matching /.test(error51)) {
|
|
35842
|
+
return "no_tab";
|
|
35843
|
+
}
|
|
35844
|
+
if (/not in domains \[/.test(error51)) {
|
|
35845
|
+
return "domain_denied";
|
|
35846
|
+
}
|
|
35847
|
+
if (/^capability .+ not granted/.test(error51)) {
|
|
35848
|
+
return "capability_denied";
|
|
35849
|
+
}
|
|
35850
|
+
if (/^(request|response) body too large:/.test(error51)) {
|
|
35851
|
+
return "body_too_large";
|
|
35852
|
+
}
|
|
35853
|
+
return "other";
|
|
35854
|
+
}
|
|
35855
|
+
|
|
35760
35856
|
// node_modules/@fetchproxy/server/dist/ws-server.js
|
|
35761
35857
|
var FetchproxyProtocolError = class extends Error {
|
|
35762
35858
|
constructor(message) {
|
|
@@ -35903,6 +35999,7 @@ var FetchproxyServer = class {
|
|
|
35903
35999
|
onPairCode: this.opts.onPairCode
|
|
35904
36000
|
});
|
|
35905
36001
|
this.hostHandle.onOwnInner((inner) => this.onInner(inner));
|
|
36002
|
+
this.hostHandle.onExtensionDisconnect(() => this.rejectAllPending());
|
|
35906
36003
|
} else {
|
|
35907
36004
|
this.role = "peer";
|
|
35908
36005
|
this.peerHandle = await startPeer({
|
|
@@ -36321,10 +36418,11 @@ var FetchproxyServer = class {
|
|
|
36321
36418
|
if (inner.op === void 0 || inner.op === "fetch") {
|
|
36322
36419
|
fetchCb({ ok: true, status: inner.status, url: inner.url, body: inner.body });
|
|
36323
36420
|
} else {
|
|
36324
|
-
|
|
36421
|
+
const error51 = `unexpected ${inner.op} response on fetch awaiter`;
|
|
36422
|
+
fetchCb({ ok: false, error: error51, kind: classifyFetchError(error51) });
|
|
36325
36423
|
}
|
|
36326
36424
|
} else {
|
|
36327
|
-
fetchCb({ ok: false, error: inner.error });
|
|
36425
|
+
fetchCb({ ok: false, error: inner.error, kind: classifyFetchError(inner.error) });
|
|
36328
36426
|
}
|
|
36329
36427
|
return;
|
|
36330
36428
|
}
|
|
@@ -36391,6 +36489,26 @@ var FetchproxyServer = class {
|
|
|
36391
36489
|
}
|
|
36392
36490
|
}
|
|
36393
36491
|
}
|
|
36492
|
+
rejectAllPending() {
|
|
36493
|
+
const err = new FetchproxyProtocolError("extension disconnected");
|
|
36494
|
+
for (const cb of this.pending.values()) {
|
|
36495
|
+
cb({ ok: false, error: err.message, kind: classifyFetchError(err.message) });
|
|
36496
|
+
}
|
|
36497
|
+
this.pending.clear();
|
|
36498
|
+
for (const cb of this.pendingReadCookies.values()) {
|
|
36499
|
+
cb({ ok: false, error: err.message });
|
|
36500
|
+
}
|
|
36501
|
+
this.pendingReadCookies.clear();
|
|
36502
|
+
for (const { reject } of this.pendingStorage.values())
|
|
36503
|
+
reject(err);
|
|
36504
|
+
this.pendingStorage.clear();
|
|
36505
|
+
for (const { reject } of this.pendingCapture.values())
|
|
36506
|
+
reject(err);
|
|
36507
|
+
this.pendingCapture.clear();
|
|
36508
|
+
for (const { reject } of this.pendingIdb.values())
|
|
36509
|
+
reject(err);
|
|
36510
|
+
this.pendingIdb.clear();
|
|
36511
|
+
}
|
|
36394
36512
|
/**
|
|
36395
36513
|
* Shut down the bridge. Host: terminates the WebSocket server and any
|
|
36396
36514
|
* still-attached extension/peer clients. Peer: closes the upstream
|
|
@@ -36398,6 +36516,7 @@ var FetchproxyServer = class {
|
|
|
36398
36516
|
* twice in a row.
|
|
36399
36517
|
*/
|
|
36400
36518
|
async close() {
|
|
36519
|
+
this.rejectAllPending();
|
|
36401
36520
|
if (this.hostHandle)
|
|
36402
36521
|
await this.hostHandle.close();
|
|
36403
36522
|
if (this.peerHandle)
|
|
@@ -36493,8 +36612,7 @@ async function bootstrap(opts) {
|
|
|
36493
36612
|
for (const p of localStoragePointers) {
|
|
36494
36613
|
pointers[p.outputKey] = { storageKey: p.storageKey, jsonPointer: p.jsonPointer };
|
|
36495
36614
|
}
|
|
36496
|
-
|
|
36497
|
-
localStorage = await stub.readLocalStorage({
|
|
36615
|
+
localStorage = await server2.readLocalStorage({
|
|
36498
36616
|
keys: allKeys,
|
|
36499
36617
|
...storageDomainOpts,
|
|
36500
36618
|
...localStoragePointers.length > 0 ? { pointers } : {}
|
|
@@ -36507,8 +36625,7 @@ async function bootstrap(opts) {
|
|
|
36507
36625
|
for (const p of sessionStoragePointers) {
|
|
36508
36626
|
pointers[p.outputKey] = { storageKey: p.storageKey, jsonPointer: p.jsonPointer };
|
|
36509
36627
|
}
|
|
36510
|
-
|
|
36511
|
-
sessionStorage = await stub.readSessionStorage({
|
|
36628
|
+
sessionStorage = await server2.readSessionStorage({
|
|
36512
36629
|
keys: allKeys,
|
|
36513
36630
|
...storageDomainOpts,
|
|
36514
36631
|
...sessionStoragePointers.length > 0 ? { pointers } : {}
|
|
@@ -36531,9 +36648,6 @@ async function bootstrap(opts) {
|
|
|
36531
36648
|
}
|
|
36532
36649
|
const indexedDbBucket = {};
|
|
36533
36650
|
for (const d of indexedDb) {
|
|
36534
|
-
if (!server2.readIndexedDb) {
|
|
36535
|
-
throw new Error("bootstrap: server factory does not implement readIndexedDb (declared indexedDb but server stub omits it)");
|
|
36536
|
-
}
|
|
36537
36651
|
const values = await server2.readIndexedDb({
|
|
36538
36652
|
database: d.database,
|
|
36539
36653
|
store: d.store,
|
|
@@ -36658,7 +36772,7 @@ function loadAccount(env = process.env) {
|
|
|
36658
36772
|
// package.json
|
|
36659
36773
|
var package_default = {
|
|
36660
36774
|
name: "canvas-parent-mcp",
|
|
36661
|
-
version: "1.
|
|
36775
|
+
version: "1.1.2",
|
|
36662
36776
|
mcpName: "io.github.chrischall/canvas-parent-mcp",
|
|
36663
36777
|
description: "Canvas LMS (Instructure) MCP server \u2014 student/observer read access with optional file download",
|
|
36664
36778
|
author: "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
@@ -36702,7 +36816,7 @@ var package_default = {
|
|
|
36702
36816
|
"test:watch": "vitest"
|
|
36703
36817
|
},
|
|
36704
36818
|
dependencies: {
|
|
36705
|
-
"@fetchproxy/bootstrap": "^0.
|
|
36819
|
+
"@fetchproxy/bootstrap": "^0.5.1",
|
|
36706
36820
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
36707
36821
|
dotenv: "^17.4.0",
|
|
36708
36822
|
zod: "^4.3.6"
|
|
@@ -37504,7 +37618,7 @@ try {
|
|
|
37504
37618
|
} catch (e) {
|
|
37505
37619
|
configError = e;
|
|
37506
37620
|
}
|
|
37507
|
-
var server = new McpServer({ name: "canvas", version: "1.
|
|
37621
|
+
var server = new McpServer({ name: "canvas", version: "1.1.2" });
|
|
37508
37622
|
if (resolved) {
|
|
37509
37623
|
const client = new CanvasClient(resolved.account, {
|
|
37510
37624
|
preloaded: resolved.preloaded
|
package/dist/index.js
CHANGED
|
@@ -44,7 +44,7 @@ try {
|
|
|
44
44
|
catch (e) {
|
|
45
45
|
configError = e;
|
|
46
46
|
}
|
|
47
|
-
const server = new McpServer({ name: 'canvas', version: '1.
|
|
47
|
+
const server = new McpServer({ name: 'canvas', version: '1.1.2' }); // x-release-please-version
|
|
48
48
|
if (resolved) {
|
|
49
49
|
const client = new CanvasClient(resolved.account, {
|
|
50
50
|
preloaded: resolved.preloaded,
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "canvas-parent-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"mcpName": "io.github.chrischall/canvas-parent-mcp",
|
|
5
|
-
"description": "Canvas LMS (Instructure) MCP server
|
|
5
|
+
"description": "Canvas LMS (Instructure) MCP server — student/observer read access with optional file download",
|
|
6
6
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"test:watch": "vitest"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@fetchproxy/bootstrap": "^0.
|
|
47
|
+
"@fetchproxy/bootstrap": "^0.5.1",
|
|
48
48
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
49
49
|
"dotenv": "^17.4.0",
|
|
50
50
|
"zod": "^4.3.6"
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/chrischall/canvas-parent-mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.1.2",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "canvas-parent-mcp",
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "1.1.2",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|