canvas-parent-mcp 2.1.9 → 2.1.10
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/.mcp.json +2 -2
- package/dist/bundle.js +183 -12
- package/dist/client.js +29 -1
- package/dist/index.js +1 -1
- package/package.json +4 -4
- 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": "2.1.
|
|
10
|
+
"version": "2.1.10"
|
|
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": "2.1.
|
|
18
|
+
"version": "2.1.10",
|
|
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": "2.1.
|
|
4
|
+
"version": "2.1.10",
|
|
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/.mcp.json
CHANGED
package/dist/bundle.js
CHANGED
|
@@ -91,6 +91,7 @@ var require_util = __commonJS({
|
|
|
91
91
|
exports.explicitlyAborted = explicitlyAborted2;
|
|
92
92
|
exports.prefixIssues = prefixIssues2;
|
|
93
93
|
exports.unwrapMessage = unwrapMessage2;
|
|
94
|
+
exports.sizeCaps = sizeCaps2;
|
|
94
95
|
exports.attachSchema = attachSchema2;
|
|
95
96
|
exports.finalizeIssue = finalizeIssue2;
|
|
96
97
|
exports.getSizableOrigin = getSizableOrigin2;
|
|
@@ -625,6 +626,18 @@ var require_util = __commonJS({
|
|
|
625
626
|
function unwrapMessage2(message) {
|
|
626
627
|
return typeof message === "string" ? message : message?.message;
|
|
627
628
|
}
|
|
629
|
+
function sizeCaps2(defChecks) {
|
|
630
|
+
const caps = [];
|
|
631
|
+
for (const ch of defChecks ?? []) {
|
|
632
|
+
const d = ch._zod.def;
|
|
633
|
+
if (!/_(length|size)$|_equals$/.test(d.check))
|
|
634
|
+
break;
|
|
635
|
+
const max = d.maximum ?? d.length ?? d.size;
|
|
636
|
+
if (max >= 0)
|
|
637
|
+
caps.push([max, ch]);
|
|
638
|
+
}
|
|
639
|
+
return caps;
|
|
640
|
+
}
|
|
628
641
|
function attachSchema2(issues, start, inst) {
|
|
629
642
|
var _a3;
|
|
630
643
|
for (let i = start; i < issues.length; i++) {
|
|
@@ -2289,7 +2302,7 @@ var require_versions = __commonJS({
|
|
|
2289
2302
|
exports.version = {
|
|
2290
2303
|
major: 4,
|
|
2291
2304
|
minor: 6,
|
|
2292
|
-
patch:
|
|
2305
|
+
patch: 7
|
|
2293
2306
|
};
|
|
2294
2307
|
(function() {
|
|
2295
2308
|
var keys = Object.getOwnPropertyNames(exports);
|
|
@@ -3123,6 +3136,31 @@ var require_schemas = __commonJS({
|
|
|
3123
3136
|
return payload;
|
|
3124
3137
|
};
|
|
3125
3138
|
});
|
|
3139
|
+
function capSize2(inst, size) {
|
|
3140
|
+
const caps = util.sizeCaps(inst._zod.def.checks);
|
|
3141
|
+
if (!caps.length)
|
|
3142
|
+
return;
|
|
3143
|
+
const cap = Math.min(...caps.map((c) => c[0]));
|
|
3144
|
+
const parse3 = inst._zod.parse;
|
|
3145
|
+
inst._zod.parse = (payload, ctx) => {
|
|
3146
|
+
const n = size(payload.value);
|
|
3147
|
+
if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
|
|
3148
|
+
for (const [max, ch] of util.sizeCaps(inst._zod.def.checks)) {
|
|
3149
|
+
if (n > max && ch._zod.def.when(payload)) {
|
|
3150
|
+
const start = payload.issues.length;
|
|
3151
|
+
ch._zod.check(payload);
|
|
3152
|
+
payload.issues[start].continue = false;
|
|
3153
|
+
util.attachSchema(payload.issues, start, inst);
|
|
3154
|
+
return payload;
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
return parse3(payload, ctx);
|
|
3159
|
+
};
|
|
3160
|
+
}
|
|
3161
|
+
var arraySize2 = (input2) => Array.isArray(input2) ? input2.length : -1;
|
|
3162
|
+
var mapSize2 = (input2) => input2 instanceof Map ? input2.size : -1;
|
|
3163
|
+
var setSize2 = (input2) => input2 instanceof Set ? input2.size : -1;
|
|
3126
3164
|
function handleArrayResult2(result, final, index) {
|
|
3127
3165
|
if (result.issues.length) {
|
|
3128
3166
|
final.issues.push(...util.prefixIssues(index, result.issues));
|
|
@@ -3166,6 +3204,7 @@ var require_schemas = __commonJS({
|
|
|
3166
3204
|
}
|
|
3167
3205
|
return payload;
|
|
3168
3206
|
};
|
|
3207
|
+
capSize2(inst, arraySize2);
|
|
3169
3208
|
});
|
|
3170
3209
|
function handlePropertyResult2(result, final, key, input2, optin, optout) {
|
|
3171
3210
|
const isPresent = key in input2;
|
|
@@ -4122,6 +4161,7 @@ var require_schemas = __commonJS({
|
|
|
4122
4161
|
return Promise.all(proms).then(() => payload);
|
|
4123
4162
|
return payload;
|
|
4124
4163
|
};
|
|
4164
|
+
capSize2(inst, mapSize2);
|
|
4125
4165
|
});
|
|
4126
4166
|
function handleMapResult2(keyResult, valueResult, final, key, input2, inst, ctx) {
|
|
4127
4167
|
if (keyResult.issues.length) {
|
|
@@ -4188,6 +4228,7 @@ var require_schemas = __commonJS({
|
|
|
4188
4228
|
return Promise.all(proms).then(() => payload);
|
|
4189
4229
|
return payload;
|
|
4190
4230
|
};
|
|
4231
|
+
capSize2(inst, setSize2);
|
|
4191
4232
|
});
|
|
4192
4233
|
function handleSetResult2(result, final) {
|
|
4193
4234
|
if (result.issues.length) {
|
|
@@ -16292,9 +16333,15 @@ ${code}
|
|
|
16292
16333
|
doc.write(`}`);
|
|
16293
16334
|
return outputVar;
|
|
16294
16335
|
}
|
|
16336
|
+
function generateSizeGate2(doc, schema, size) {
|
|
16337
|
+
for (const [max, check2] of util.sizeCaps(schema._zod.def.checks)) {
|
|
16338
|
+
doc.write(`if (${size} > ${numericOperand2(max, check2._zod.def.check)}) return INVALID;`);
|
|
16339
|
+
}
|
|
16340
|
+
}
|
|
16295
16341
|
function generateArrayCheck2(doc, ctx, schema, accessor, buildsValue = true) {
|
|
16296
16342
|
const def = schema._zod.def;
|
|
16297
16343
|
doc.write(`if (!Array.isArray(${accessor})) return INVALID;`);
|
|
16344
|
+
generateSizeGate2(doc, schema, `${accessor}.length`);
|
|
16298
16345
|
const outputVar = buildsValue ? newVar2(ctx) : null;
|
|
16299
16346
|
const iVar = newVar2(ctx);
|
|
16300
16347
|
const elemVar = newVar2(ctx);
|
|
@@ -16694,6 +16741,7 @@ ${code}
|
|
|
16694
16741
|
function generateMapCheck2(doc, ctx, schema, accessor) {
|
|
16695
16742
|
const def = schema._zod.def;
|
|
16696
16743
|
doc.write(`if (!(${accessor} instanceof Map)) return INVALID;`);
|
|
16744
|
+
generateSizeGate2(doc, schema, `${accessor}.size`);
|
|
16697
16745
|
const outputVar = newVar2(ctx);
|
|
16698
16746
|
const kVar = newVar2(ctx);
|
|
16699
16747
|
const valVar = newVar2(ctx);
|
|
@@ -16710,6 +16758,7 @@ ${code}
|
|
|
16710
16758
|
function generateSetCheck2(doc, ctx, schema, accessor) {
|
|
16711
16759
|
const def = schema._zod.def;
|
|
16712
16760
|
doc.write(`if (!(${accessor} instanceof Set)) return INVALID;`);
|
|
16761
|
+
generateSizeGate2(doc, schema, `${accessor}.size`);
|
|
16713
16762
|
const outputVar = newVar2(ctx);
|
|
16714
16763
|
const valVar = newVar2(ctx);
|
|
16715
16764
|
doc.write(`const ${outputVar} = new Set();`);
|
|
@@ -27078,7 +27127,7 @@ function declaredDialect(schema, remedy) {
|
|
|
27078
27127
|
throw new Error(`JSON Schema declares an unsupported dialect ("$schema": "${schema.$schema.slice(0, 200)}"). The default validator supports JSON Schema 2020-12, 2019-09, draft-07, and draft-06; ${remedy}`);
|
|
27079
27128
|
}
|
|
27080
27129
|
|
|
27081
|
-
// node_modules/@modelcontextprotocol/core/dist/auth-BNDyLTqp.mjs
|
|
27130
|
+
// node_modules/@modelcontextprotocol/server/node_modules/@modelcontextprotocol/core/dist/auth-BNDyLTqp.mjs
|
|
27082
27131
|
var z = __toESM(require_v4(), 1);
|
|
27083
27132
|
var LATEST_PROTOCOL_VERSION = "2025-11-25";
|
|
27084
27133
|
var SUPPORTED_PROTOCOL_VERSIONS = [
|
|
@@ -41874,7 +41923,7 @@ var API_KEY_RE = new RegExp([
|
|
|
41874
41923
|
// webhook signing secret (Stripe-style)
|
|
41875
41924
|
].map((p) => `\\b${p}`).join("|"), "g");
|
|
41876
41925
|
var GOOGLE_OAUTH_TOKEN_RE = /(?<![A-Za-z0-9+/])(?:ya29\.|1\/\/)[A-Za-z0-9._-]{8,}/g;
|
|
41877
|
-
var QUERY_SECRET_RE = new RegExp(`([
|
|
41926
|
+
var QUERY_SECRET_RE = new RegExp(`([?&#;](?:(?:access|refresh|id|auth|session|csrf|xsrf|api)[_-]?token|client[_-]?secret|api[_-]?secret|api[_-]?key|password|passwd|signature|secret|token|auth|key|sig|(?:code[_-]?)?verifier|(?:php|j)?sess(?:ion)?[_-]?id|sid|x[-_]${HEADER_NAME_RUN}?(?:api[-_]?key|token|secret|auth)${HEADER_NAME_RUN})=)[^&#\\s"'<>\`]+`, "gi");
|
|
41878
41927
|
var OAUTH_CODE_RE = /([?&]code=)(?=[^&#\s"'<>`]{16,})[^&#\s"'<>`]+/gi;
|
|
41879
41928
|
var FORM_PASSWORD_RE = /((?:^|[\s,;:"'(\[{])(?:password|passwd)=)[^&#\s"'<>`]+/gim;
|
|
41880
41929
|
var HEADER_SECRET_RE = new RegExp(`(\\b(?:x[-_]${HEADER_NAME_RUN}?(?:api[-_]?key|token|secret|auth)${HEADER_NAME_RUN}|api[-_]?key)\\s*:\\s*)[^\\s,;"'<>\`]+`, "gi");
|
|
@@ -41885,6 +41934,8 @@ var JSON_SECRET_KEYS = [
|
|
|
41885
41934
|
"api[_-]?secret",
|
|
41886
41935
|
"(?:x[_-])?api[_-]?key",
|
|
41887
41936
|
"private[_-]?key",
|
|
41937
|
+
"aws[_-]?secret[_-]?access[_-]?key",
|
|
41938
|
+
// fleet-audit#1297
|
|
41888
41939
|
"code[_-]?verifier",
|
|
41889
41940
|
// PKCE; a bare `verifier` key is too generic for JSON
|
|
41890
41941
|
`x[_-]${HEADER_NAME_RUN}?(?:api[_-]?key|token|secret|auth)${HEADER_NAME_RUN}`,
|
|
@@ -42582,6 +42633,7 @@ __export(util_exports, {
|
|
|
42582
42633
|
required: () => required,
|
|
42583
42634
|
safeExtend: () => safeExtend,
|
|
42584
42635
|
shallowClone: () => shallowClone,
|
|
42636
|
+
sizeCaps: () => sizeCaps,
|
|
42585
42637
|
slugify: () => slugify,
|
|
42586
42638
|
stringifyPrimitive: () => stringifyPrimitive,
|
|
42587
42639
|
toZod: () => toZod,
|
|
@@ -43101,6 +43153,18 @@ function prefixIssues(path, issues) {
|
|
|
43101
43153
|
function unwrapMessage(message) {
|
|
43102
43154
|
return typeof message === "string" ? message : message?.message;
|
|
43103
43155
|
}
|
|
43156
|
+
function sizeCaps(defChecks) {
|
|
43157
|
+
const caps = [];
|
|
43158
|
+
for (const ch of defChecks ?? []) {
|
|
43159
|
+
const d = ch._zod.def;
|
|
43160
|
+
if (!/_(length|size)$|_equals$/.test(d.check))
|
|
43161
|
+
break;
|
|
43162
|
+
const max = d.maximum ?? d.length ?? d.size;
|
|
43163
|
+
if (max >= 0)
|
|
43164
|
+
caps.push([max, ch]);
|
|
43165
|
+
}
|
|
43166
|
+
return caps;
|
|
43167
|
+
}
|
|
43104
43168
|
function attachSchema(issues, start, inst) {
|
|
43105
43169
|
var _a3;
|
|
43106
43170
|
for (let i = start; i < issues.length; i++) {
|
|
@@ -44521,7 +44585,7 @@ ${content.join("\n")}
|
|
|
44521
44585
|
var version = {
|
|
44522
44586
|
major: 4,
|
|
44523
44587
|
minor: 6,
|
|
44524
|
-
patch:
|
|
44588
|
+
patch: 7
|
|
44525
44589
|
};
|
|
44526
44590
|
|
|
44527
44591
|
// node_modules/zod/v4/core/schemas.js
|
|
@@ -45277,6 +45341,31 @@ var $ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
|
|
|
45277
45341
|
return payload;
|
|
45278
45342
|
};
|
|
45279
45343
|
});
|
|
45344
|
+
function capSize(inst, size) {
|
|
45345
|
+
const caps = sizeCaps(inst._zod.def.checks);
|
|
45346
|
+
if (!caps.length)
|
|
45347
|
+
return;
|
|
45348
|
+
const cap = Math.min(...caps.map((c) => c[0]));
|
|
45349
|
+
const parse3 = inst._zod.parse;
|
|
45350
|
+
inst._zod.parse = (payload, ctx) => {
|
|
45351
|
+
const n = size(payload.value);
|
|
45352
|
+
if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
|
|
45353
|
+
for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
|
|
45354
|
+
if (n > max && ch._zod.def.when(payload)) {
|
|
45355
|
+
const start = payload.issues.length;
|
|
45356
|
+
ch._zod.check(payload);
|
|
45357
|
+
payload.issues[start].continue = false;
|
|
45358
|
+
attachSchema(payload.issues, start, inst);
|
|
45359
|
+
return payload;
|
|
45360
|
+
}
|
|
45361
|
+
}
|
|
45362
|
+
}
|
|
45363
|
+
return parse3(payload, ctx);
|
|
45364
|
+
};
|
|
45365
|
+
}
|
|
45366
|
+
var arraySize = (input2) => Array.isArray(input2) ? input2.length : -1;
|
|
45367
|
+
var mapSize = (input2) => input2 instanceof Map ? input2.size : -1;
|
|
45368
|
+
var setSize = (input2) => input2 instanceof Set ? input2.size : -1;
|
|
45280
45369
|
function handleArrayResult(result, final, index) {
|
|
45281
45370
|
if (result.issues.length) {
|
|
45282
45371
|
final.issues.push(...prefixIssues(index, result.issues));
|
|
@@ -45320,6 +45409,7 @@ var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
|
|
|
45320
45409
|
}
|
|
45321
45410
|
return payload;
|
|
45322
45411
|
};
|
|
45412
|
+
capSize(inst, arraySize);
|
|
45323
45413
|
});
|
|
45324
45414
|
function handlePropertyResult(result, final, key, input2, optin, optout) {
|
|
45325
45415
|
const isPresent = key in input2;
|
|
@@ -46276,6 +46366,7 @@ var $ZodMap = /* @__PURE__ */ $constructor("$ZodMap", (inst, def) => {
|
|
|
46276
46366
|
return Promise.all(proms).then(() => payload);
|
|
46277
46367
|
return payload;
|
|
46278
46368
|
};
|
|
46369
|
+
capSize(inst, mapSize);
|
|
46279
46370
|
});
|
|
46280
46371
|
function handleMapResult(keyResult, valueResult, final, key, input2, inst, ctx) {
|
|
46281
46372
|
if (keyResult.issues.length) {
|
|
@@ -46342,6 +46433,7 @@ var $ZodSet = /* @__PURE__ */ $constructor("$ZodSet", (inst, def) => {
|
|
|
46342
46433
|
return Promise.all(proms).then(() => payload);
|
|
46343
46434
|
return payload;
|
|
46344
46435
|
};
|
|
46436
|
+
capSize(inst, setSize);
|
|
46345
46437
|
});
|
|
46346
46438
|
function handleSetResult(result, final) {
|
|
46347
46439
|
if (result.issues.length) {
|
|
@@ -55872,9 +55964,15 @@ function generateNullableCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
|
55872
55964
|
doc.write(`}`);
|
|
55873
55965
|
return outputVar;
|
|
55874
55966
|
}
|
|
55967
|
+
function generateSizeGate(doc, schema, size) {
|
|
55968
|
+
for (const [max, check2] of sizeCaps(schema._zod.def.checks)) {
|
|
55969
|
+
doc.write(`if (${size} > ${numericOperand(max, check2._zod.def.check)}) return INVALID;`);
|
|
55970
|
+
}
|
|
55971
|
+
}
|
|
55875
55972
|
function generateArrayCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
55876
55973
|
const def = schema._zod.def;
|
|
55877
55974
|
doc.write(`if (!Array.isArray(${accessor})) return INVALID;`);
|
|
55975
|
+
generateSizeGate(doc, schema, `${accessor}.length`);
|
|
55878
55976
|
const outputVar = buildsValue ? newVar(ctx) : null;
|
|
55879
55977
|
const iVar = newVar(ctx);
|
|
55880
55978
|
const elemVar = newVar(ctx);
|
|
@@ -56274,6 +56372,7 @@ function literalPropertyKey(ctx, key) {
|
|
|
56274
56372
|
function generateMapCheck(doc, ctx, schema, accessor) {
|
|
56275
56373
|
const def = schema._zod.def;
|
|
56276
56374
|
doc.write(`if (!(${accessor} instanceof Map)) return INVALID;`);
|
|
56375
|
+
generateSizeGate(doc, schema, `${accessor}.size`);
|
|
56277
56376
|
const outputVar = newVar(ctx);
|
|
56278
56377
|
const kVar = newVar(ctx);
|
|
56279
56378
|
const valVar = newVar(ctx);
|
|
@@ -56290,6 +56389,7 @@ function generateMapCheck(doc, ctx, schema, accessor) {
|
|
|
56290
56389
|
function generateSetCheck(doc, ctx, schema, accessor) {
|
|
56291
56390
|
const def = schema._zod.def;
|
|
56292
56391
|
doc.write(`if (!(${accessor} instanceof Set)) return INVALID;`);
|
|
56392
|
+
generateSizeGate(doc, schema, `${accessor}.size`);
|
|
56293
56393
|
const outputVar = newVar(ctx);
|
|
56294
56394
|
const valVar = newVar(ctx);
|
|
56295
56395
|
doc.write(`const ${outputVar} = new Set();`);
|
|
@@ -62002,10 +62102,34 @@ import { constants as fsConstants2, realpathSync as realpathSync2 } from "node:f
|
|
|
62002
62102
|
var O_NOFOLLOW2 = fsConstants2.O_NOFOLLOW ?? 0;
|
|
62003
62103
|
var O_NONBLOCK = fsConstants2.O_NONBLOCK ?? 0;
|
|
62004
62104
|
|
|
62105
|
+
// node_modules/@chrischall/mcp-utils/dist/internal/ascii.js
|
|
62106
|
+
function asciiLower(s) {
|
|
62107
|
+
return s.replace(/[A-Z]+/g, (m) => m.toLowerCase());
|
|
62108
|
+
}
|
|
62109
|
+
|
|
62005
62110
|
// node_modules/@chrischall/mcp-utils/dist/scrape/index.js
|
|
62006
62111
|
var NEXT_DATA_MAX_CHARS = 16 * 1024 * 1024;
|
|
62007
62112
|
function isCloudflareChallenge(html) {
|
|
62008
|
-
return
|
|
62113
|
+
return html.includes("_cf_chl_opt") || hasJustAMomentTitle(html);
|
|
62114
|
+
}
|
|
62115
|
+
var JUST_A_MOMENT = "just a moment";
|
|
62116
|
+
function hasJustAMomentTitle(html) {
|
|
62117
|
+
const lower = asciiLower(html);
|
|
62118
|
+
let i = 0;
|
|
62119
|
+
for (; ; ) {
|
|
62120
|
+
const open3 = lower.indexOf("<title", i);
|
|
62121
|
+
if (open3 < 0)
|
|
62122
|
+
return false;
|
|
62123
|
+
const gt = lower.indexOf(">", open3 + 6);
|
|
62124
|
+
if (gt < 0)
|
|
62125
|
+
return false;
|
|
62126
|
+
let j = gt + 1;
|
|
62127
|
+
while (j < lower.length && /\s/.test(lower[j]))
|
|
62128
|
+
j++;
|
|
62129
|
+
if (lower.startsWith(JUST_A_MOMENT, j))
|
|
62130
|
+
return true;
|
|
62131
|
+
i = gt + 1;
|
|
62132
|
+
}
|
|
62009
62133
|
}
|
|
62010
62134
|
|
|
62011
62135
|
// node_modules/@chrischall/mcp-utils/dist/http/index.js
|
|
@@ -62869,6 +62993,17 @@ function isPublicSuffix(host) {
|
|
|
62869
62993
|
return false;
|
|
62870
62994
|
}
|
|
62871
62995
|
|
|
62996
|
+
// node_modules/@fetchproxy/protocol/dist/ip-literal.js
|
|
62997
|
+
function isIpLiteralHost(host) {
|
|
62998
|
+
if (host.includes(":") || host.startsWith("["))
|
|
62999
|
+
return true;
|
|
63000
|
+
const labels = host.split(".");
|
|
63001
|
+
if (labels.length > 1 && labels[labels.length - 1] === "")
|
|
63002
|
+
labels.pop();
|
|
63003
|
+
const last = labels[labels.length - 1] ?? "";
|
|
63004
|
+
return /^[0-9]+$/.test(last) || /^0x[0-9a-f]*$/i.test(last);
|
|
63005
|
+
}
|
|
63006
|
+
|
|
62872
63007
|
// node_modules/@fetchproxy/protocol/dist/validate.js
|
|
62873
63008
|
var ProtocolError3 = class extends Error {
|
|
62874
63009
|
constructor(message) {
|
|
@@ -63334,6 +63469,9 @@ function validateHello(raw) {
|
|
|
63334
63469
|
if (!HOSTNAME_RE.test(d)) {
|
|
63335
63470
|
throw new ProtocolError3(`hello.domains: invalid hostname ${JSON.stringify(d)}`);
|
|
63336
63471
|
}
|
|
63472
|
+
if (isIpLiteralHost(d)) {
|
|
63473
|
+
throw new ProtocolError3(`hello.domains: ${JSON.stringify(d)} is an IP address \u2014 declare the site's DNS hostname; IP-literal origins (LAN, loopback, link-local) are not reachable through the bridge`);
|
|
63474
|
+
}
|
|
63337
63475
|
if (isPublicSuffix(d)) {
|
|
63338
63476
|
throw new ProtocolError3(`hello.domains: ${JSON.stringify(d)} is a public suffix \u2014 declare a registrable domain (e.g. "example.${d}"), not the suffix every site under it shares`);
|
|
63339
63477
|
}
|
|
@@ -65459,7 +65597,7 @@ async function startHost(opts) {
|
|
|
65459
65597
|
if (ownRetryableRefusal)
|
|
65460
65598
|
resetSessionPromise();
|
|
65461
65599
|
resolveOwnSession(ownSession);
|
|
65462
|
-
} else {
|
|
65600
|
+
} else if (identified === "extension" && extensionWs === ws) {
|
|
65463
65601
|
const slot = peers.get(frame.mcpId);
|
|
65464
65602
|
if (slot)
|
|
65465
65603
|
slot.ws.send(JSON.stringify(frame));
|
|
@@ -66485,6 +66623,9 @@ var FetchproxyServer = class {
|
|
|
66485
66623
|
throw new Error("FetchproxyServer: opts.domains must be a non-empty array of hostnames");
|
|
66486
66624
|
}
|
|
66487
66625
|
for (const d of opts.domains) {
|
|
66626
|
+
if (typeof d === "string" && isIpLiteralHost(d)) {
|
|
66627
|
+
throw new Error(`FetchproxyServer: opts.domains entry ${JSON.stringify(d)} is an IP address \u2014 declare the site's DNS hostname; IP-literal origins (LAN, loopback, link-local) are not reachable through the bridge`);
|
|
66628
|
+
}
|
|
66488
66629
|
if (typeof d === "string" && isPublicSuffix(d)) {
|
|
66489
66630
|
throw new Error(`FetchproxyServer: opts.domains entry ${JSON.stringify(d)} is a public suffix \u2014 declare a registrable domain (e.g. "example.${d}"), not the suffix every site under it shares`);
|
|
66490
66631
|
}
|
|
@@ -68419,6 +68560,9 @@ var FetchproxyServer = class {
|
|
|
68419
68560
|
}
|
|
68420
68561
|
};
|
|
68421
68562
|
|
|
68563
|
+
// node_modules/@fetchproxy/server/dist/bot-wall.js
|
|
68564
|
+
var CLOUDFLARE_TITLE_SCAN_MAX = 64 * 1024;
|
|
68565
|
+
|
|
68422
68566
|
// node_modules/@fetchproxy/bootstrap/dist/index.js
|
|
68423
68567
|
var defaultFactory = (opts) => new FetchproxyServer(opts);
|
|
68424
68568
|
function createSessionLifter(opts) {
|
|
@@ -68704,7 +68848,7 @@ function loadAccount(env = process.env) {
|
|
|
68704
68848
|
// package.json
|
|
68705
68849
|
var package_default = {
|
|
68706
68850
|
name: "canvas-parent-mcp",
|
|
68707
|
-
version: "2.1.
|
|
68851
|
+
version: "2.1.10",
|
|
68708
68852
|
mcpName: "io.github.chrischall/canvas-parent-mcp",
|
|
68709
68853
|
description: "Canvas LMS (Instructure) MCP server \u2014 student/observer read access with optional file download",
|
|
68710
68854
|
author: "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
@@ -68751,9 +68895,9 @@ var package_default = {
|
|
|
68751
68895
|
typecheck: "tsc -p tsconfig.json --noEmit"
|
|
68752
68896
|
},
|
|
68753
68897
|
dependencies: {
|
|
68754
|
-
"@chrischall/mcp-utils": "^3.0.
|
|
68755
|
-
"@fetchproxy/bootstrap": "^3.6.
|
|
68756
|
-
"@fetchproxy/server": "^3.6.
|
|
68898
|
+
"@chrischall/mcp-utils": "^3.0.3",
|
|
68899
|
+
"@fetchproxy/bootstrap": "^3.6.2",
|
|
68900
|
+
"@fetchproxy/server": "^3.6.2",
|
|
68757
68901
|
"@modelcontextprotocol/server": "^2.3.0",
|
|
68758
68902
|
dotenv: "^18.0.0",
|
|
68759
68903
|
zod: "^4.6.5"
|
|
@@ -69813,10 +69957,29 @@ var CanvasClient = class {
|
|
|
69813
69957
|
const linkHeader = res.headers.get("link");
|
|
69814
69958
|
const next = linkHeader ? parseLinkHeader(linkHeader).next : void 0;
|
|
69815
69959
|
if (!next) break;
|
|
69816
|
-
url4 = next;
|
|
69960
|
+
url4 = this.pinPaginationUrl(next);
|
|
69817
69961
|
}
|
|
69818
69962
|
return out;
|
|
69819
69963
|
}
|
|
69964
|
+
/**
|
|
69965
|
+
* The Link header is upstream-controlled, and the next hop carries the
|
|
69966
|
+
* parent's credential (Bearer token or session cookies). Only follow it on
|
|
69967
|
+
* the configured Canvas origin over https; anything else is refused rather
|
|
69968
|
+
* than fetched — a truncated list reported as complete would mislead too.
|
|
69969
|
+
*/
|
|
69970
|
+
pinPaginationUrl(next) {
|
|
69971
|
+
const base = new URL(this.account.baseUrl);
|
|
69972
|
+
let parsed;
|
|
69973
|
+
try {
|
|
69974
|
+
parsed = new URL(next, base);
|
|
69975
|
+
} catch {
|
|
69976
|
+
throw new PaginationOriginError(base.origin);
|
|
69977
|
+
}
|
|
69978
|
+
if (parsed.protocol !== "https:" || parsed.origin !== base.origin || parsed.username || parsed.password) {
|
|
69979
|
+
throw new PaginationOriginError(base.origin);
|
|
69980
|
+
}
|
|
69981
|
+
return parsed.href;
|
|
69982
|
+
}
|
|
69820
69983
|
/**
|
|
69821
69984
|
* Download a Canvas file to disk.
|
|
69822
69985
|
*
|
|
@@ -70076,6 +70239,14 @@ var CanvasUnreachableError = class extends Error {
|
|
|
70076
70239
|
}
|
|
70077
70240
|
status;
|
|
70078
70241
|
};
|
|
70242
|
+
var PaginationOriginError = class extends Error {
|
|
70243
|
+
constructor(origin) {
|
|
70244
|
+
super(`PaginationOriginRejected: Canvas sent a next-page link off ${origin}; stopped without following it (the request carries your Canvas credential).`);
|
|
70245
|
+
this.origin = origin;
|
|
70246
|
+
this.name = "PaginationOriginError";
|
|
70247
|
+
}
|
|
70248
|
+
origin;
|
|
70249
|
+
};
|
|
70079
70250
|
var DownloadUrlRejectedError = class extends Error {
|
|
70080
70251
|
constructor(url4, reason) {
|
|
70081
70252
|
super(`DownloadUrlRejected: ${reason}; use the \`url\` from canvas_list_course_files. Got: ${url4}`);
|
|
@@ -70560,7 +70731,7 @@ try {
|
|
|
70560
70731
|
var client = resolved ? new CanvasClient(resolved.account, { refreshSession: resolved.refresh }) : null;
|
|
70561
70732
|
await runMcp({
|
|
70562
70733
|
name: "canvas",
|
|
70563
|
-
version: "2.1.
|
|
70734
|
+
version: "2.1.10",
|
|
70564
70735
|
// x-release-please-version
|
|
70565
70736
|
tools: [
|
|
70566
70737
|
// Outside the `if (resolved)` on purpose: an unconfigured server must
|
package/dist/client.js
CHANGED
|
@@ -90,10 +90,30 @@ export class CanvasClient {
|
|
|
90
90
|
const next = linkHeader ? parseLinkHeader(linkHeader).next : undefined;
|
|
91
91
|
if (!next)
|
|
92
92
|
break;
|
|
93
|
-
url = next;
|
|
93
|
+
url = this.pinPaginationUrl(next);
|
|
94
94
|
}
|
|
95
95
|
return out;
|
|
96
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* The Link header is upstream-controlled, and the next hop carries the
|
|
99
|
+
* parent's credential (Bearer token or session cookies). Only follow it on
|
|
100
|
+
* the configured Canvas origin over https; anything else is refused rather
|
|
101
|
+
* than fetched — a truncated list reported as complete would mislead too.
|
|
102
|
+
*/
|
|
103
|
+
pinPaginationUrl(next) {
|
|
104
|
+
const base = new URL(this.account.baseUrl);
|
|
105
|
+
let parsed;
|
|
106
|
+
try {
|
|
107
|
+
parsed = new URL(next, base);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
throw new PaginationOriginError(base.origin);
|
|
111
|
+
}
|
|
112
|
+
if (parsed.protocol !== 'https:' || parsed.origin !== base.origin || parsed.username || parsed.password) {
|
|
113
|
+
throw new PaginationOriginError(base.origin);
|
|
114
|
+
}
|
|
115
|
+
return parsed.href;
|
|
116
|
+
}
|
|
97
117
|
/**
|
|
98
118
|
* Download a Canvas file to disk.
|
|
99
119
|
*
|
|
@@ -409,6 +429,14 @@ export class CanvasUnreachableError extends Error {
|
|
|
409
429
|
this.name = 'CanvasUnreachableError';
|
|
410
430
|
}
|
|
411
431
|
}
|
|
432
|
+
export class PaginationOriginError extends Error {
|
|
433
|
+
origin;
|
|
434
|
+
constructor(origin) {
|
|
435
|
+
super(`PaginationOriginRejected: Canvas sent a next-page link off ${origin}; stopped without following it (the request carries your Canvas credential).`);
|
|
436
|
+
this.origin = origin;
|
|
437
|
+
this.name = 'PaginationOriginError';
|
|
438
|
+
}
|
|
439
|
+
}
|
|
412
440
|
export class DownloadUrlRejectedError extends Error {
|
|
413
441
|
url;
|
|
414
442
|
constructor(url, reason) {
|
package/dist/index.js
CHANGED
|
@@ -54,7 +54,7 @@ const client = resolved
|
|
|
54
54
|
// host's install-time `tools/list` succeeds.
|
|
55
55
|
await runMcp({
|
|
56
56
|
name: 'canvas',
|
|
57
|
-
version: '2.1.
|
|
57
|
+
version: '2.1.10', // x-release-please-version
|
|
58
58
|
tools: [
|
|
59
59
|
// Outside the `if (resolved)` on purpose: an unconfigured server must
|
|
60
60
|
// still expose something that can say WHY (see the tool's docblock).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "canvas-parent-mcp",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.10",
|
|
4
4
|
"mcpName": "io.github.chrischall/canvas-parent-mcp",
|
|
5
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>",
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@chrischall/mcp-utils": "^3.0.
|
|
51
|
-
"@fetchproxy/bootstrap": "^3.6.
|
|
52
|
-
"@fetchproxy/server": "^3.6.
|
|
50
|
+
"@chrischall/mcp-utils": "^3.0.3",
|
|
51
|
+
"@fetchproxy/bootstrap": "^3.6.2",
|
|
52
|
+
"@fetchproxy/server": "^3.6.2",
|
|
53
53
|
"@modelcontextprotocol/server": "^2.3.0",
|
|
54
54
|
"dotenv": "^18.0.0",
|
|
55
55
|
"zod": "^4.6.5"
|
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": "2.1.
|
|
9
|
+
"version": "2.1.10",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "canvas-parent-mcp",
|
|
14
|
-
"version": "2.1.
|
|
14
|
+
"version": "2.1.10",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|