canvas-parent-mcp 2.1.2 → 2.1.4
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 +2 -0
- package/dist/bundle.js +278 -135
- package/dist/client.js +129 -10
- package/dist/index.js +1 -1
- package/dist/tools/conversations.js +5 -1
- package/dist/tools/files.js +4 -4
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/canvas-parent/SKILL.md +1 -1
- package/skills/canvas-parent-api/references/canvas-endpoints.md +3 -1
package/dist/bundle.js
CHANGED
|
@@ -4858,7 +4858,7 @@ var require_memoizer = __commonJS({
|
|
|
4858
4858
|
var NONE2 = 0;
|
|
4859
4859
|
var ASSUMED2 = 1;
|
|
4860
4860
|
var PROVEN2 = 2;
|
|
4861
|
-
function isRecursive2(inst, stack,
|
|
4861
|
+
function isRecursive2(inst, stack, resolve5) {
|
|
4862
4862
|
const cached2 = recursive2.get(inst);
|
|
4863
4863
|
if (cached2 !== void 0)
|
|
4864
4864
|
return cached2 ? PROVEN2 : NONE2;
|
|
@@ -4868,7 +4868,7 @@ var require_memoizer = __commonJS({
|
|
|
4868
4868
|
let result = NONE2;
|
|
4869
4869
|
const check2 = (child) => {
|
|
4870
4870
|
if (result !== PROVEN2 && child?._zod) {
|
|
4871
|
-
const answer = isRecursive2(child, stack,
|
|
4871
|
+
const answer = isRecursive2(child, stack, resolve5);
|
|
4872
4872
|
if (answer > result)
|
|
4873
4873
|
result = answer;
|
|
4874
4874
|
}
|
|
@@ -4879,7 +4879,7 @@ var require_memoizer = __commonJS({
|
|
|
4879
4879
|
const desc = Object.getOwnPropertyDescriptor(sh, key);
|
|
4880
4880
|
if (spread && !desc.enumerable)
|
|
4881
4881
|
continue;
|
|
4882
|
-
const child = desc.get ? ASSUMED2 : desc.value?._zod ? isRecursive2(desc.value, stack,
|
|
4882
|
+
const child = desc.get ? ASSUMED2 : desc.value?._zod ? isRecursive2(desc.value, stack, resolve5) : NONE2;
|
|
4883
4883
|
if (child > answer)
|
|
4884
4884
|
answer = child;
|
|
4885
4885
|
}
|
|
@@ -4943,7 +4943,7 @@ var require_memoizer = __commonJS({
|
|
|
4943
4943
|
break;
|
|
4944
4944
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
4945
4945
|
case "lazy": {
|
|
4946
|
-
const inner = def._cachedInner ?? (
|
|
4946
|
+
const inner = def._cachedInner ?? (resolve5 ? inst._zod.innerType : void 0);
|
|
4947
4947
|
merge2(inner ? isRecursive2(inner, stack, false) : ASSUMED2);
|
|
4948
4948
|
break;
|
|
4949
4949
|
}
|
|
@@ -5007,7 +5007,7 @@ var require_memoizer = __commonJS({
|
|
|
5007
5007
|
return bucket;
|
|
5008
5008
|
}
|
|
5009
5009
|
var handoff2;
|
|
5010
|
-
var
|
|
5010
|
+
var open3 = [];
|
|
5011
5011
|
var memo3 = {
|
|
5012
5012
|
alloc(_inst, payload, empty) {
|
|
5013
5013
|
const bucket = handoff2;
|
|
@@ -5016,7 +5016,7 @@ var require_memoizer = __commonJS({
|
|
|
5016
5016
|
handoff2 = void 0;
|
|
5017
5017
|
const entry = { value: empty, issues: null };
|
|
5018
5018
|
bucket.set(payload.value, entry);
|
|
5019
|
-
|
|
5019
|
+
open3.push(entry);
|
|
5020
5020
|
return empty;
|
|
5021
5021
|
},
|
|
5022
5022
|
guard(inst) {
|
|
@@ -5087,10 +5087,10 @@ var require_memoizer = __commonJS({
|
|
|
5087
5087
|
return payload;
|
|
5088
5088
|
}
|
|
5089
5089
|
handoff2 = bucket;
|
|
5090
|
-
const depth =
|
|
5090
|
+
const depth = open3.length;
|
|
5091
5091
|
const result = base(payload, ctx);
|
|
5092
5092
|
handoff2 = void 0;
|
|
5093
|
-
const entry =
|
|
5093
|
+
const entry = open3.length > depth ? open3.pop() : void 0;
|
|
5094
5094
|
if (result instanceof Promise) {
|
|
5095
5095
|
return result.then((r) => {
|
|
5096
5096
|
if (entry)
|
|
@@ -26497,7 +26497,7 @@ var require_stream = __commonJS({
|
|
|
26497
26497
|
};
|
|
26498
26498
|
duplex._final = function(callback) {
|
|
26499
26499
|
if (ws.readyState === ws.CONNECTING) {
|
|
26500
|
-
ws.once("open", function
|
|
26500
|
+
ws.once("open", function open3() {
|
|
26501
26501
|
duplex._final(callback);
|
|
26502
26502
|
});
|
|
26503
26503
|
return;
|
|
@@ -26518,7 +26518,7 @@ var require_stream = __commonJS({
|
|
|
26518
26518
|
};
|
|
26519
26519
|
duplex._write = function(chunk2, encoding, callback) {
|
|
26520
26520
|
if (ws.readyState === ws.CONNECTING) {
|
|
26521
|
-
ws.once("open", function
|
|
26521
|
+
ws.once("open", function open3() {
|
|
26522
26522
|
duplex._write(chunk2, encoding, callback);
|
|
26523
26523
|
});
|
|
26524
26524
|
return;
|
|
@@ -26980,7 +26980,7 @@ var require_websocket_server = __commonJS({
|
|
|
26980
26980
|
});
|
|
26981
26981
|
|
|
26982
26982
|
// src/index.ts
|
|
26983
|
-
import { dirname as
|
|
26983
|
+
import { dirname as dirname4, join as join6 } from "path";
|
|
26984
26984
|
import { fileURLToPath } from "url";
|
|
26985
26985
|
|
|
26986
26986
|
// node_modules/@chrischall/mcp-utils/dist/cancel/index.js
|
|
@@ -31399,14 +31399,14 @@ function inputRequiredRoundsExceededMessage(method, maxRounds) {
|
|
|
31399
31399
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
31400
31400
|
}
|
|
31401
31401
|
function sleep(ms, signal) {
|
|
31402
|
-
return new Promise((
|
|
31402
|
+
return new Promise((resolve5, reject) => {
|
|
31403
31403
|
if (signal?.aborted) {
|
|
31404
31404
|
reject(signal.reason instanceof SdkError ? signal.reason : new SdkError(SdkErrorCode.RequestTimeout, String(signal.reason)));
|
|
31405
31405
|
return;
|
|
31406
31406
|
}
|
|
31407
31407
|
const timer = setTimeout(() => {
|
|
31408
31408
|
signal?.removeEventListener("abort", onAbort);
|
|
31409
|
-
|
|
31409
|
+
resolve5();
|
|
31410
31410
|
}, ms);
|
|
31411
31411
|
const onAbort = () => {
|
|
31412
31412
|
clearTimeout(timer);
|
|
@@ -32200,7 +32200,7 @@ var Protocol = class {
|
|
|
32200
32200
|
const flowStartedAt = Date.now();
|
|
32201
32201
|
let onAbort;
|
|
32202
32202
|
let cleanupMessageId;
|
|
32203
|
-
return new Promise((
|
|
32203
|
+
return new Promise((resolve5, reject) => {
|
|
32204
32204
|
const earlyReject = (error62) => {
|
|
32205
32205
|
reject(error62);
|
|
32206
32206
|
};
|
|
@@ -32268,7 +32268,7 @@ var Protocol = class {
|
|
|
32268
32268
|
}
|
|
32269
32269
|
if (decoded.kind === "invalid") return reject(decoded.error);
|
|
32270
32270
|
if (decoded.kind === "input_required") {
|
|
32271
|
-
if (options?.allowInputRequired === true) return
|
|
32271
|
+
if (options?.allowInputRequired === true) return resolve5(manualInputRequiredValue(decoded));
|
|
32272
32272
|
const flow = {
|
|
32273
32273
|
codec: codec2,
|
|
32274
32274
|
request,
|
|
@@ -32280,11 +32280,11 @@ var Protocol = class {
|
|
|
32280
32280
|
params
|
|
32281
32281
|
}, resultSchema, legOptions)
|
|
32282
32282
|
};
|
|
32283
|
-
return
|
|
32283
|
+
return resolve5(this._resolveNonCompleteResult(decoded, flow));
|
|
32284
32284
|
}
|
|
32285
32285
|
const result = decoded.result;
|
|
32286
32286
|
validateStandardSchema(resultSchema, result).then((parseResult) => {
|
|
32287
|
-
if (parseResult.success)
|
|
32287
|
+
if (parseResult.success) resolve5(parseResult.data);
|
|
32288
32288
|
else reject(new SdkError(SdkErrorCode.InvalidResult, `Invalid result for ${request.method}: ${parseResult.error}`));
|
|
32289
32289
|
}, reject);
|
|
32290
32290
|
});
|
|
@@ -35196,7 +35196,7 @@ var require_compile2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
35196
35196
|
ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref);
|
|
35197
35197
|
const schOrFunc = root.refs[ref];
|
|
35198
35198
|
if (schOrFunc) return schOrFunc;
|
|
35199
|
-
let _sch =
|
|
35199
|
+
let _sch = resolve5.call(this, root, ref);
|
|
35200
35200
|
if (_sch === void 0) {
|
|
35201
35201
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
35202
35202
|
const { schemaId } = this.opts;
|
|
@@ -35222,7 +35222,7 @@ var require_compile2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
35222
35222
|
function sameSchemaEnv(s1, s2) {
|
|
35223
35223
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
35224
35224
|
}
|
|
35225
|
-
function
|
|
35225
|
+
function resolve5(root, ref) {
|
|
35226
35226
|
let sch;
|
|
35227
35227
|
while (typeof (sch = this.refs[ref]) == "string") ref = sch;
|
|
35228
35228
|
return sch || this.schemas[ref] || resolveSchema.call(this, root, ref);
|
|
@@ -35672,47 +35672,47 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
35672
35672
|
else if (typeof uri === "object") uri = parse3(serialize(uri, options), options);
|
|
35673
35673
|
return uri;
|
|
35674
35674
|
}
|
|
35675
|
-
function
|
|
35675
|
+
function resolve5(baseURI, relativeURI, options) {
|
|
35676
35676
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
35677
35677
|
const resolved2 = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
35678
35678
|
schemelessOptions.skipEscape = true;
|
|
35679
35679
|
return serialize(resolved2, schemelessOptions);
|
|
35680
35680
|
}
|
|
35681
|
-
function resolveComponent(base,
|
|
35681
|
+
function resolveComponent(base, relative2, options, skipNormalization) {
|
|
35682
35682
|
const target = {};
|
|
35683
35683
|
if (!skipNormalization) {
|
|
35684
35684
|
base = parse3(serialize(base, options), options);
|
|
35685
|
-
|
|
35685
|
+
relative2 = parse3(serialize(relative2, options), options);
|
|
35686
35686
|
}
|
|
35687
35687
|
options = options || {};
|
|
35688
|
-
if (!options.tolerant &&
|
|
35689
|
-
target.scheme =
|
|
35690
|
-
target.userinfo =
|
|
35691
|
-
target.host =
|
|
35692
|
-
target.port =
|
|
35693
|
-
target.path = removeDotSegments(
|
|
35694
|
-
target.query =
|
|
35688
|
+
if (!options.tolerant && relative2.scheme) {
|
|
35689
|
+
target.scheme = relative2.scheme;
|
|
35690
|
+
target.userinfo = relative2.userinfo;
|
|
35691
|
+
target.host = relative2.host;
|
|
35692
|
+
target.port = relative2.port;
|
|
35693
|
+
target.path = removeDotSegments(relative2.path || "");
|
|
35694
|
+
target.query = relative2.query;
|
|
35695
35695
|
} else {
|
|
35696
|
-
if (
|
|
35697
|
-
target.userinfo =
|
|
35698
|
-
target.host =
|
|
35699
|
-
target.port =
|
|
35700
|
-
target.path = removeDotSegments(
|
|
35701
|
-
target.query =
|
|
35696
|
+
if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
|
|
35697
|
+
target.userinfo = relative2.userinfo;
|
|
35698
|
+
target.host = relative2.host;
|
|
35699
|
+
target.port = relative2.port;
|
|
35700
|
+
target.path = removeDotSegments(relative2.path || "");
|
|
35701
|
+
target.query = relative2.query;
|
|
35702
35702
|
} else {
|
|
35703
|
-
if (!
|
|
35703
|
+
if (!relative2.path) {
|
|
35704
35704
|
target.path = base.path;
|
|
35705
|
-
if (
|
|
35705
|
+
if (relative2.query !== void 0) target.query = relative2.query;
|
|
35706
35706
|
else target.query = base.query;
|
|
35707
35707
|
} else {
|
|
35708
|
-
if (
|
|
35708
|
+
if (relative2.path[0] === "/") target.path = removeDotSegments(relative2.path);
|
|
35709
35709
|
else {
|
|
35710
|
-
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) target.path = "/" +
|
|
35711
|
-
else if (!base.path) target.path =
|
|
35712
|
-
else target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
35710
|
+
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) target.path = "/" + relative2.path;
|
|
35711
|
+
else if (!base.path) target.path = relative2.path;
|
|
35712
|
+
else target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative2.path;
|
|
35713
35713
|
target.path = removeDotSegments(target.path);
|
|
35714
35714
|
}
|
|
35715
|
-
target.query =
|
|
35715
|
+
target.query = relative2.query;
|
|
35716
35716
|
}
|
|
35717
35717
|
target.userinfo = base.userinfo;
|
|
35718
35718
|
target.host = base.host;
|
|
@@ -35720,7 +35720,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
35720
35720
|
}
|
|
35721
35721
|
target.scheme = base.scheme;
|
|
35722
35722
|
}
|
|
35723
|
-
target.fragment =
|
|
35723
|
+
target.fragment = relative2.fragment;
|
|
35724
35724
|
return target;
|
|
35725
35725
|
}
|
|
35726
35726
|
function equal(uriA, uriB, options) {
|
|
@@ -35846,7 +35846,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
35846
35846
|
const fastUri = {
|
|
35847
35847
|
SCHEMES,
|
|
35848
35848
|
normalize,
|
|
35849
|
-
resolve:
|
|
35849
|
+
resolve: resolve5,
|
|
35850
35850
|
resolveComponent,
|
|
35851
35851
|
equal,
|
|
35852
35852
|
serialize,
|
|
@@ -41219,7 +41219,7 @@ var StdioServerTransport = class {
|
|
|
41219
41219
|
}
|
|
41220
41220
|
send(message) {
|
|
41221
41221
|
if (this._closed) return Promise.reject(/* @__PURE__ */ new Error("StdioServerTransport is closed"));
|
|
41222
|
-
return new Promise((
|
|
41222
|
+
return new Promise((resolve5, reject) => {
|
|
41223
41223
|
const json2 = serializeMessage(message);
|
|
41224
41224
|
let settled = false;
|
|
41225
41225
|
const onError = (error62) => {
|
|
@@ -41234,14 +41234,14 @@ var StdioServerTransport = class {
|
|
|
41234
41234
|
settled = true;
|
|
41235
41235
|
this._stdout.off("error", onError);
|
|
41236
41236
|
this._stdout.off("drain", onDrain);
|
|
41237
|
-
|
|
41237
|
+
resolve5();
|
|
41238
41238
|
};
|
|
41239
41239
|
this._stdout.once("error", onError);
|
|
41240
41240
|
if (this._stdout.write(json2)) {
|
|
41241
41241
|
if (settled) return;
|
|
41242
41242
|
settled = true;
|
|
41243
41243
|
this._stdout.off("error", onError);
|
|
41244
|
-
|
|
41244
|
+
resolve5();
|
|
41245
41245
|
} else if (!settled) this._stdout.once("drain", onDrain);
|
|
41246
41246
|
});
|
|
41247
41247
|
}
|
|
@@ -41295,14 +41295,14 @@ var StdioConnectionChannel = class {
|
|
|
41295
41295
|
*/
|
|
41296
41296
|
async whenRequestsAnswered(timeoutMs) {
|
|
41297
41297
|
if (this._closed || this._pendingRequests.size === 0) return true;
|
|
41298
|
-
return await new Promise((
|
|
41298
|
+
return await new Promise((resolve5) => {
|
|
41299
41299
|
const waiter = () => {
|
|
41300
41300
|
clearTimeout(timer);
|
|
41301
|
-
|
|
41301
|
+
resolve5(true);
|
|
41302
41302
|
};
|
|
41303
41303
|
const timer = setTimeout(() => {
|
|
41304
41304
|
this._drainWaiters = this._drainWaiters.filter((pending) => pending !== waiter);
|
|
41305
|
-
|
|
41305
|
+
resolve5(false);
|
|
41306
41306
|
}, timeoutMs);
|
|
41307
41307
|
this._drainWaiters.push(waiter);
|
|
41308
41308
|
});
|
|
@@ -46738,7 +46738,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
|
|
|
46738
46738
|
var NONE = 0;
|
|
46739
46739
|
var ASSUMED = 1;
|
|
46740
46740
|
var PROVEN = 2;
|
|
46741
|
-
function isRecursive(inst, stack,
|
|
46741
|
+
function isRecursive(inst, stack, resolve5) {
|
|
46742
46742
|
const cached2 = recursive.get(inst);
|
|
46743
46743
|
if (cached2 !== void 0)
|
|
46744
46744
|
return cached2 ? PROVEN : NONE;
|
|
@@ -46748,7 +46748,7 @@ function isRecursive(inst, stack, resolve2) {
|
|
|
46748
46748
|
let result = NONE;
|
|
46749
46749
|
const check2 = (child) => {
|
|
46750
46750
|
if (result !== PROVEN && child?._zod) {
|
|
46751
|
-
const answer = isRecursive(child, stack,
|
|
46751
|
+
const answer = isRecursive(child, stack, resolve5);
|
|
46752
46752
|
if (answer > result)
|
|
46753
46753
|
result = answer;
|
|
46754
46754
|
}
|
|
@@ -46759,7 +46759,7 @@ function isRecursive(inst, stack, resolve2) {
|
|
|
46759
46759
|
const desc = Object.getOwnPropertyDescriptor(sh, key);
|
|
46760
46760
|
if (spread && !desc.enumerable)
|
|
46761
46761
|
continue;
|
|
46762
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
46762
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve5) : NONE;
|
|
46763
46763
|
if (child > answer)
|
|
46764
46764
|
answer = child;
|
|
46765
46765
|
}
|
|
@@ -46823,7 +46823,7 @@ function isRecursive(inst, stack, resolve2) {
|
|
|
46823
46823
|
break;
|
|
46824
46824
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
46825
46825
|
case "lazy": {
|
|
46826
|
-
const inner = def._cachedInner ?? (
|
|
46826
|
+
const inner = def._cachedInner ?? (resolve5 ? inst._zod.innerType : void 0);
|
|
46827
46827
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
46828
46828
|
break;
|
|
46829
46829
|
}
|
|
@@ -61639,6 +61639,8 @@ function runMcp(opts) {
|
|
|
61639
61639
|
}
|
|
61640
61640
|
|
|
61641
61641
|
// node_modules/@chrischall/mcp-utils/dist/config/index.js
|
|
61642
|
+
import { homedir } from "node:os";
|
|
61643
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
61642
61644
|
var PLACEHOLDER_RE = /^\$\{[^}]*\}$/;
|
|
61643
61645
|
function readEnvVar(key, opts = {}) {
|
|
61644
61646
|
const env = opts.env ?? process.env;
|
|
@@ -61665,6 +61667,15 @@ function parseBoolEnv(key, opts = {}) {
|
|
|
61665
61667
|
return false;
|
|
61666
61668
|
return fallback;
|
|
61667
61669
|
}
|
|
61670
|
+
function expandPath(p) {
|
|
61671
|
+
let expanded = p;
|
|
61672
|
+
if (p === "~") {
|
|
61673
|
+
expanded = homedir();
|
|
61674
|
+
} else if (p.startsWith("~/")) {
|
|
61675
|
+
expanded = join(homedir(), p.slice(2));
|
|
61676
|
+
}
|
|
61677
|
+
return isAbsolute(expanded) ? expanded : resolve(expanded);
|
|
61678
|
+
}
|
|
61668
61679
|
async function loadDotenvSafely(opts = {}) {
|
|
61669
61680
|
try {
|
|
61670
61681
|
const mod = await import(
|
|
@@ -61682,6 +61693,41 @@ async function loadDotenvSafely(opts = {}) {
|
|
|
61682
61693
|
}
|
|
61683
61694
|
}
|
|
61684
61695
|
|
|
61696
|
+
// node_modules/@chrischall/mcp-utils/dist/fs/confine.js
|
|
61697
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
61698
|
+
import { dirname, isAbsolute as isAbsolute2, relative, resolve as resolve2, sep } from "node:path";
|
|
61699
|
+
function isInside(root, target) {
|
|
61700
|
+
const rel = relative(root, target);
|
|
61701
|
+
return rel === "" || !rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute2(rel);
|
|
61702
|
+
}
|
|
61703
|
+
function realPathAllowingMissing(p) {
|
|
61704
|
+
const abs = resolve2(p);
|
|
61705
|
+
let existing = abs;
|
|
61706
|
+
const tail = [];
|
|
61707
|
+
while (!existsSync(existing)) {
|
|
61708
|
+
const parent = dirname(existing);
|
|
61709
|
+
if (parent === existing)
|
|
61710
|
+
break;
|
|
61711
|
+
tail.unshift(existing.slice(parent.length).replace(/^[/\\]+/, ""));
|
|
61712
|
+
existing = parent;
|
|
61713
|
+
}
|
|
61714
|
+
let real;
|
|
61715
|
+
try {
|
|
61716
|
+
real = realpathSync(existing);
|
|
61717
|
+
} catch {
|
|
61718
|
+
real = existing;
|
|
61719
|
+
}
|
|
61720
|
+
return tail.length > 0 ? resolve2(real, ...tail) : real;
|
|
61721
|
+
}
|
|
61722
|
+
function assertPathWithinRoots(path, roots) {
|
|
61723
|
+
const target = realPathAllowingMissing(expandPath(path));
|
|
61724
|
+
for (const root of roots) {
|
|
61725
|
+
if (isInside(realPathAllowingMissing(expandPath(root)), target))
|
|
61726
|
+
return target;
|
|
61727
|
+
}
|
|
61728
|
+
throw new Error(`Path is outside the allowed directories: ${path}`);
|
|
61729
|
+
}
|
|
61730
|
+
|
|
61685
61731
|
// node_modules/@chrischall/mcp-utils/dist/http/index.js
|
|
61686
61732
|
function buildQueryString(params) {
|
|
61687
61733
|
const parts = [];
|
|
@@ -63495,7 +63541,7 @@ var DEFAULT_BIND_TIMEOUT_MS = 5e3;
|
|
|
63495
63541
|
async function electRole(opts) {
|
|
63496
63542
|
const server = createServer();
|
|
63497
63543
|
const bindTimeoutMs = opts.bindTimeoutMs ?? DEFAULT_BIND_TIMEOUT_MS;
|
|
63498
|
-
return new Promise((
|
|
63544
|
+
return new Promise((resolve5, reject) => {
|
|
63499
63545
|
let timer;
|
|
63500
63546
|
const clearBindTimer = () => {
|
|
63501
63547
|
if (timer) {
|
|
@@ -63511,7 +63557,7 @@ async function electRole(opts) {
|
|
|
63511
63557
|
server.close();
|
|
63512
63558
|
} catch {
|
|
63513
63559
|
}
|
|
63514
|
-
|
|
63560
|
+
resolve5({ role: "peer" });
|
|
63515
63561
|
} else {
|
|
63516
63562
|
reject(e);
|
|
63517
63563
|
}
|
|
@@ -63519,7 +63565,7 @@ async function electRole(opts) {
|
|
|
63519
63565
|
const onListening = () => {
|
|
63520
63566
|
clearBindTimer();
|
|
63521
63567
|
server.removeListener("error", onError);
|
|
63522
|
-
|
|
63568
|
+
resolve5({ role: "host", server });
|
|
63523
63569
|
};
|
|
63524
63570
|
server.once("error", onError);
|
|
63525
63571
|
server.once("listening", onListening);
|
|
@@ -63798,13 +63844,13 @@ async function awaitSessionReady(ready, opts) {
|
|
|
63798
63844
|
// node_modules/@fetchproxy/server/dist/extension-trust.js
|
|
63799
63845
|
import { readFile as readFile2, writeFile as writeFile2, rename as rename2, unlink as unlink2, mkdir as mkdir2, chmod as chmod2, rm as rm2 } from "node:fs/promises";
|
|
63800
63846
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
63801
|
-
import { isAbsolute as
|
|
63847
|
+
import { isAbsolute as isAbsolute4, join as join3 } from "node:path";
|
|
63802
63848
|
|
|
63803
63849
|
// node_modules/@fetchproxy/server/dist/identity.js
|
|
63804
63850
|
import { readFile, readdir, writeFile, mkdir, chmod, rename, rm, unlink, link, lstat } from "node:fs/promises";
|
|
63805
63851
|
import { randomBytes } from "node:crypto";
|
|
63806
|
-
import { isAbsolute, join } from "node:path";
|
|
63807
|
-
import { homedir } from "node:os";
|
|
63852
|
+
import { isAbsolute as isAbsolute3, join as join2 } from "node:path";
|
|
63853
|
+
import { homedir as homedir2 } from "node:os";
|
|
63808
63854
|
var SAFE_PLAIN = /^[A-Za-z0-9._-]+$/;
|
|
63809
63855
|
var SAFE_SCOPED = /^@[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
|
|
63810
63856
|
function envIdentityDir() {
|
|
@@ -63812,12 +63858,12 @@ function envIdentityDir() {
|
|
|
63812
63858
|
if (raw === void 0)
|
|
63813
63859
|
return void 0;
|
|
63814
63860
|
const dir = raw.trim();
|
|
63815
|
-
if (dir === "" || !
|
|
63861
|
+
if (dir === "" || !isAbsolute3(dir))
|
|
63816
63862
|
return void 0;
|
|
63817
63863
|
return dir;
|
|
63818
63864
|
}
|
|
63819
63865
|
function defaultIdentityDir() {
|
|
63820
|
-
return envIdentityDir() ??
|
|
63866
|
+
return envIdentityDir() ?? join2(homedir2(), ".fetchproxy", "identity");
|
|
63821
63867
|
}
|
|
63822
63868
|
function safeIdentityFileBase(serverName) {
|
|
63823
63869
|
if (!serverName || serverName === ".." || serverName.includes("..") || !SAFE_PLAIN.test(serverName) && !SAFE_SCOPED.test(serverName)) {
|
|
@@ -63826,7 +63872,7 @@ function safeIdentityFileBase(serverName) {
|
|
|
63826
63872
|
return serverName.replace(/\//g, "_");
|
|
63827
63873
|
}
|
|
63828
63874
|
function identityFilePath(dir, serverName) {
|
|
63829
|
-
return
|
|
63875
|
+
return join2(dir, `${safeIdentityFileBase(serverName)}.json`);
|
|
63830
63876
|
}
|
|
63831
63877
|
async function generateIdentity() {
|
|
63832
63878
|
const x = await generateX25519();
|
|
@@ -63880,7 +63926,7 @@ async function sweepStagingFiles(dir, serverName, opts = {}) {
|
|
|
63880
63926
|
}
|
|
63881
63927
|
const cutoff = opts.olderThanMs === void 0 ? null : Date.now() - opts.olderThanMs;
|
|
63882
63928
|
await Promise.all(names.filter((name) => staging.test(name)).map(async (name) => {
|
|
63883
|
-
const path =
|
|
63929
|
+
const path = join2(dir, name);
|
|
63884
63930
|
if (cutoff !== null) {
|
|
63885
63931
|
const st = await lstat(path).catch(() => null);
|
|
63886
63932
|
if (st === null || st.mtimeMs > cutoff)
|
|
@@ -63961,7 +64007,7 @@ function envTrustDir(env = process.env) {
|
|
|
63961
64007
|
if (raw === void 0)
|
|
63962
64008
|
return void 0;
|
|
63963
64009
|
const dir = raw.trim();
|
|
63964
|
-
if (dir !== "" &&
|
|
64010
|
+
if (dir !== "" && isAbsolute4(dir))
|
|
63965
64011
|
return dir;
|
|
63966
64012
|
console.warn(`[fetchproxy] ignoring ${TRUST_DIR_ENV}=${JSON.stringify(raw)}: it is not an absolute path. The extension pin will be written beside the identity instead, which is not where you pointed it \u2014 if that directory is read-only, no pin is ever kept.`);
|
|
63967
64013
|
return void 0;
|
|
@@ -64009,13 +64055,13 @@ function decideExtensionTrust(args) {
|
|
|
64009
64055
|
};
|
|
64010
64056
|
}
|
|
64011
64057
|
function extensionTrustPath(serverName, dir = defaultTrustDir()) {
|
|
64012
|
-
return
|
|
64058
|
+
return join3(dir, `${safeIdentityFileBase(serverName)}.extension-trust.json`);
|
|
64013
64059
|
}
|
|
64014
64060
|
function extensionTrustPathHint(serverName) {
|
|
64015
64061
|
try {
|
|
64016
64062
|
return extensionTrustPath(serverName);
|
|
64017
64063
|
} catch {
|
|
64018
|
-
return
|
|
64064
|
+
return join3(defaultTrustDir(), "<server-name>.extension-trust.json");
|
|
64019
64065
|
}
|
|
64020
64066
|
}
|
|
64021
64067
|
function isPin(x) {
|
|
@@ -64168,8 +64214,8 @@ async function startHost(opts) {
|
|
|
64168
64214
|
let rejectOwnSession;
|
|
64169
64215
|
let ownSessionReady;
|
|
64170
64216
|
function resetSessionPromise() {
|
|
64171
|
-
ownSessionReady = new Promise((
|
|
64172
|
-
resolveOwnSession =
|
|
64217
|
+
ownSessionReady = new Promise((resolve5, reject) => {
|
|
64218
|
+
resolveOwnSession = resolve5;
|
|
64173
64219
|
rejectOwnSession = reject;
|
|
64174
64220
|
});
|
|
64175
64221
|
ownSessionReady.catch(() => {
|
|
@@ -64571,7 +64617,7 @@ async function startHost(opts) {
|
|
|
64571
64617
|
});
|
|
64572
64618
|
});
|
|
64573
64619
|
return {
|
|
64574
|
-
close: () => new Promise((
|
|
64620
|
+
close: () => new Promise((resolve5) => {
|
|
64575
64621
|
for (const client of wss.clients) {
|
|
64576
64622
|
try {
|
|
64577
64623
|
client.terminate();
|
|
@@ -64579,7 +64625,7 @@ async function startHost(opts) {
|
|
|
64579
64625
|
}
|
|
64580
64626
|
}
|
|
64581
64627
|
wss.close(() => {
|
|
64582
|
-
opts.httpServer.close(() =>
|
|
64628
|
+
opts.httpServer.close(() => resolve5());
|
|
64583
64629
|
});
|
|
64584
64630
|
}),
|
|
64585
64631
|
sendOwnInner: async (inner) => {
|
|
@@ -64619,11 +64665,11 @@ async function startPeer(opts) {
|
|
|
64619
64665
|
maxPayload: opts.maxPayloadBytes ?? MAX_PAYLOAD_BYTES
|
|
64620
64666
|
});
|
|
64621
64667
|
const dialTimeoutMs = opts.dialTimeoutMs ?? PEER_DIAL_TIMEOUT_MS;
|
|
64622
|
-
await new Promise((
|
|
64668
|
+
await new Promise((resolve5, reject) => {
|
|
64623
64669
|
let timer;
|
|
64624
64670
|
const onOpen = () => {
|
|
64625
64671
|
cleanup();
|
|
64626
|
-
|
|
64672
|
+
resolve5();
|
|
64627
64673
|
};
|
|
64628
64674
|
const onError = (e) => {
|
|
64629
64675
|
cleanup();
|
|
@@ -64711,10 +64757,10 @@ async function startPeer(opts) {
|
|
|
64711
64757
|
let sessionPromiseSettled = false;
|
|
64712
64758
|
function resetSessionPromise() {
|
|
64713
64759
|
sessionPromiseSettled = false;
|
|
64714
|
-
sessionPromise = new Promise((
|
|
64760
|
+
sessionPromise = new Promise((resolve5, reject) => {
|
|
64715
64761
|
resolveFirstReady = (st) => {
|
|
64716
64762
|
sessionPromiseSettled = true;
|
|
64717
|
-
|
|
64763
|
+
resolve5(st);
|
|
64718
64764
|
};
|
|
64719
64765
|
rejectFirstReady = (e) => {
|
|
64720
64766
|
sessionPromiseSettled = true;
|
|
@@ -65864,8 +65910,8 @@ var FetchproxyServer = class {
|
|
|
65864
65910
|
async _fetchOnceWithTimeout(init, fetchOpts = {}) {
|
|
65865
65911
|
const id = this.nextRequestId++;
|
|
65866
65912
|
const inner = { type: "request", id, op: "fetch", init };
|
|
65867
|
-
const pending = this.guardPending(new Promise((
|
|
65868
|
-
this.pending.set(id,
|
|
65913
|
+
const pending = this.guardPending(new Promise((resolve5) => {
|
|
65914
|
+
this.pending.set(id, resolve5);
|
|
65869
65915
|
}));
|
|
65870
65916
|
try {
|
|
65871
65917
|
await this.sendInnerFrame(inner, {
|
|
@@ -65888,12 +65934,12 @@ var FetchproxyServer = class {
|
|
|
65888
65934
|
try {
|
|
65889
65935
|
return await Promise.race([
|
|
65890
65936
|
pending,
|
|
65891
|
-
new Promise((
|
|
65937
|
+
new Promise((resolve5) => {
|
|
65892
65938
|
timer = setTimeout(() => {
|
|
65893
65939
|
this.pending.delete(id);
|
|
65894
65940
|
const elapsedMs = Date.now() - start;
|
|
65895
65941
|
const error62 = `fetchproxy: ${init.url} did not respond within ${timeoutMs}ms`;
|
|
65896
|
-
|
|
65942
|
+
resolve5({
|
|
65897
65943
|
ok: false,
|
|
65898
65944
|
error: error62,
|
|
65899
65945
|
kind: "timeout",
|
|
@@ -66037,9 +66083,9 @@ var FetchproxyServer = class {
|
|
|
66037
66083
|
if (opts.subdomain !== void 0)
|
|
66038
66084
|
assertSubdomainLabel(opts.subdomain);
|
|
66039
66085
|
const baseDomain = this.resolveBaseDomain(opts.domain);
|
|
66040
|
-
const
|
|
66086
|
+
const isAbsolute6 = path.startsWith("http://") || path.startsWith("https://");
|
|
66041
66087
|
let host;
|
|
66042
|
-
if (
|
|
66088
|
+
if (isAbsolute6) {
|
|
66043
66089
|
try {
|
|
66044
66090
|
host = new URL(path).host;
|
|
66045
66091
|
} catch {
|
|
@@ -66048,7 +66094,7 @@ var FetchproxyServer = class {
|
|
|
66048
66094
|
} else {
|
|
66049
66095
|
host = opts.subdomain ? `${opts.subdomain}.${baseDomain}` : baseDomain;
|
|
66050
66096
|
}
|
|
66051
|
-
const url4 =
|
|
66097
|
+
const url4 = isAbsolute6 ? path : `https://${host}${path}`;
|
|
66052
66098
|
assertUrlInDomains("request url", url4, this.opts.domains);
|
|
66053
66099
|
let tabUrl = `https://${host}/`;
|
|
66054
66100
|
if (opts.viaTab !== void 0) {
|
|
@@ -66313,8 +66359,8 @@ var FetchproxyServer = class {
|
|
|
66313
66359
|
const tabUrl = `https://${host}/`;
|
|
66314
66360
|
inner = { type: "request", id, op: "read_cookies", init: { tabUrl } };
|
|
66315
66361
|
}
|
|
66316
|
-
const pending = this.guardPending(new Promise((
|
|
66317
|
-
this.pendingReadCookies.set(id,
|
|
66362
|
+
const pending = this.guardPending(new Promise((resolve5) => {
|
|
66363
|
+
this.pendingReadCookies.set(id, resolve5);
|
|
66318
66364
|
}));
|
|
66319
66365
|
await this.sendInnerFrame(inner);
|
|
66320
66366
|
const result = await this._withVerbTimeout(pending, this.pendingReadCookies, id, `https://${host}`);
|
|
@@ -66372,8 +66418,8 @@ var FetchproxyServer = class {
|
|
|
66372
66418
|
...cookiePath !== void 0 ? { path: cookiePath } : {}
|
|
66373
66419
|
}
|
|
66374
66420
|
};
|
|
66375
|
-
const pending = this.guardPending(new Promise((
|
|
66376
|
-
this.pendingWriteCookies.set(id, { resolve:
|
|
66421
|
+
const pending = this.guardPending(new Promise((resolve5, reject) => {
|
|
66422
|
+
this.pendingWriteCookies.set(id, { resolve: resolve5, reject });
|
|
66377
66423
|
}));
|
|
66378
66424
|
await this.sendInnerFrame(inner);
|
|
66379
66425
|
return this._withVerbTimeout(
|
|
@@ -66443,8 +66489,8 @@ var FetchproxyServer = class {
|
|
|
66443
66489
|
...opts.pointers ? { pointers: { ...opts.pointers } } : {}
|
|
66444
66490
|
}
|
|
66445
66491
|
};
|
|
66446
|
-
const pending = this.guardPending(new Promise((
|
|
66447
|
-
this.pendingStorage.set(id, { resolve:
|
|
66492
|
+
const pending = this.guardPending(new Promise((resolve5, reject) => {
|
|
66493
|
+
this.pendingStorage.set(id, { resolve: resolve5, reject });
|
|
66448
66494
|
}));
|
|
66449
66495
|
await this.sendInnerFrame(inner);
|
|
66450
66496
|
return this._withVerbTimeout(pending, this.pendingStorage, id, `https://${host}`);
|
|
@@ -66502,8 +66548,8 @@ var FetchproxyServer = class {
|
|
|
66502
66548
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {}
|
|
66503
66549
|
}
|
|
66504
66550
|
};
|
|
66505
|
-
const pending = this.guardPending(new Promise((
|
|
66506
|
-
this.pendingCapture.set(id, { resolve:
|
|
66551
|
+
const pending = this.guardPending(new Promise((resolve5, reject) => {
|
|
66552
|
+
this.pendingCapture.set(id, { resolve: resolve5, reject });
|
|
66507
66553
|
}));
|
|
66508
66554
|
await this.sendInnerFrame(inner);
|
|
66509
66555
|
return this._withVerbTimeout(
|
|
@@ -66550,8 +66596,8 @@ var FetchproxyServer = class {
|
|
|
66550
66596
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {}
|
|
66551
66597
|
}
|
|
66552
66598
|
};
|
|
66553
|
-
const pending = this.guardPending(new Promise((
|
|
66554
|
-
this.pendingRedirect.set(id, { resolve:
|
|
66599
|
+
const pending = this.guardPending(new Promise((resolve5, reject) => {
|
|
66600
|
+
this.pendingRedirect.set(id, { resolve: resolve5, reject });
|
|
66555
66601
|
}));
|
|
66556
66602
|
await this.sendInnerFrame(inner);
|
|
66557
66603
|
return this._withVerbTimeout(
|
|
@@ -66646,8 +66692,8 @@ var FetchproxyServer = class {
|
|
|
66646
66692
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {}
|
|
66647
66693
|
}
|
|
66648
66694
|
};
|
|
66649
|
-
const pending = this.guardPending(new Promise((
|
|
66650
|
-
this.pendingDownload.set(id, { resolve:
|
|
66695
|
+
const pending = this.guardPending(new Promise((resolve5, reject) => {
|
|
66696
|
+
this.pendingDownload.set(id, { resolve: resolve5, reject });
|
|
66651
66697
|
}));
|
|
66652
66698
|
await this.sendInnerFrame(inner);
|
|
66653
66699
|
return this._withVerbTimeout(
|
|
@@ -66707,8 +66753,8 @@ var FetchproxyServer = class {
|
|
|
66707
66753
|
keys: [...opts.keys]
|
|
66708
66754
|
}
|
|
66709
66755
|
};
|
|
66710
|
-
const pending = this.guardPending(new Promise((
|
|
66711
|
-
this.pendingIdb.set(id, { resolve:
|
|
66756
|
+
const pending = this.guardPending(new Promise((resolve5, reject) => {
|
|
66757
|
+
this.pendingIdb.set(id, { resolve: resolve5, reject });
|
|
66712
66758
|
}));
|
|
66713
66759
|
await this.sendInnerFrame(inner);
|
|
66714
66760
|
return this._withVerbTimeout(pending, this.pendingIdb, id, origin);
|
|
@@ -66747,8 +66793,8 @@ var FetchproxyServer = class {
|
|
|
66747
66793
|
op: "read_dom",
|
|
66748
66794
|
init: { origin, names: [...opts.names] }
|
|
66749
66795
|
};
|
|
66750
|
-
const pending = this.guardPending(new Promise((
|
|
66751
|
-
this.pendingStorage.set(id, { resolve:
|
|
66796
|
+
const pending = this.guardPending(new Promise((resolve5, reject) => {
|
|
66797
|
+
this.pendingStorage.set(id, { resolve: resolve5, reject });
|
|
66752
66798
|
}));
|
|
66753
66799
|
await this.sendInnerFrame(inner);
|
|
66754
66800
|
return this._withVerbTimeout(pending, this.pendingStorage, id, origin);
|
|
@@ -66793,8 +66839,8 @@ var FetchproxyServer = class {
|
|
|
66793
66839
|
op: "read_dom_list",
|
|
66794
66840
|
init: { origin, name: opts.name }
|
|
66795
66841
|
};
|
|
66796
|
-
const pending = this.guardPending(new Promise((
|
|
66797
|
-
this.pendingDomList.set(id, { resolve:
|
|
66842
|
+
const pending = this.guardPending(new Promise((resolve5, reject) => {
|
|
66843
|
+
this.pendingDomList.set(id, { resolve: resolve5, reject });
|
|
66798
66844
|
}));
|
|
66799
66845
|
await this.sendInnerFrame(inner);
|
|
66800
66846
|
return this._withVerbTimeout(pending, this.pendingDomList, id, origin);
|
|
@@ -66839,8 +66885,8 @@ var FetchproxyServer = class {
|
|
|
66839
66885
|
...opts.tabUrl !== void 0 ? { tabUrl: opts.tabUrl } : {}
|
|
66840
66886
|
}
|
|
66841
66887
|
};
|
|
66842
|
-
const pending = this.guardPending(new Promise((
|
|
66843
|
-
this.pendingGraphql.set(id, { resolve:
|
|
66888
|
+
const pending = this.guardPending(new Promise((resolve5, reject) => {
|
|
66889
|
+
this.pendingGraphql.set(id, { resolve: resolve5, reject });
|
|
66844
66890
|
}));
|
|
66845
66891
|
const retrySafe = opts.retryOnTimeout === true;
|
|
66846
66892
|
await this.sendInnerFrame(inner, { resendable: retrySafe });
|
|
@@ -67535,7 +67581,7 @@ function loadAccount(env = process.env) {
|
|
|
67535
67581
|
// package.json
|
|
67536
67582
|
var package_default = {
|
|
67537
67583
|
name: "canvas-parent-mcp",
|
|
67538
|
-
version: "2.1.
|
|
67584
|
+
version: "2.1.4",
|
|
67539
67585
|
mcpName: "io.github.chrischall/canvas-parent-mcp",
|
|
67540
67586
|
description: "Canvas LMS (Instructure) MCP server \u2014 student/observer read access with optional file download",
|
|
67541
67587
|
author: "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
@@ -67841,12 +67887,13 @@ function registerCredentialHealthcheckTool(args) {
|
|
|
67841
67887
|
}
|
|
67842
67888
|
|
|
67843
67889
|
// src/client.ts
|
|
67844
|
-
import {
|
|
67890
|
+
import { constants as fsConstants } from "fs";
|
|
67891
|
+
import { lstat as lstat2, open as open2, stat } from "fs/promises";
|
|
67845
67892
|
|
|
67846
67893
|
// node_modules/@chrischall/mcp-utils/dist/session/index.js
|
|
67847
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync, renameSync, unlinkSync } from "node:fs";
|
|
67848
|
-
import { dirname, join as
|
|
67849
|
-
import { homedir as
|
|
67894
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync, chmodSync, renameSync, unlinkSync } from "node:fs";
|
|
67895
|
+
import { dirname as dirname2, join as join4, resolve as resolve3 } from "node:path";
|
|
67896
|
+
import { homedir as homedir3 } from "node:os";
|
|
67850
67897
|
import { randomBytes as randomBytes3, createHmac } from "node:crypto";
|
|
67851
67898
|
function bindingDigest(secret, salt) {
|
|
67852
67899
|
return createHmac("sha256", salt).update(secret).digest("hex").slice(0, 32);
|
|
@@ -67868,7 +67915,7 @@ function createFileStatePersistence(opts) {
|
|
|
67868
67915
|
const secret = opts.boundTo;
|
|
67869
67916
|
return {
|
|
67870
67917
|
load() {
|
|
67871
|
-
if (!
|
|
67918
|
+
if (!existsSync2(filePath))
|
|
67872
67919
|
return null;
|
|
67873
67920
|
try {
|
|
67874
67921
|
const raw = JSON.parse(readFileSync(filePath, "utf8"));
|
|
@@ -67898,9 +67945,9 @@ function createFileStatePersistence(opts) {
|
|
|
67898
67945
|
}
|
|
67899
67946
|
},
|
|
67900
67947
|
save(state) {
|
|
67901
|
-
const dir =
|
|
67948
|
+
const dir = dirname2(filePath);
|
|
67902
67949
|
const tmp = `${filePath}.tmp-${randomBytes3(6).toString("hex")}`;
|
|
67903
|
-
const dirExisted =
|
|
67950
|
+
const dirExisted = existsSync2(dir);
|
|
67904
67951
|
try {
|
|
67905
67952
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
67906
67953
|
if (!dirExisted)
|
|
@@ -67917,7 +67964,7 @@ function createFileStatePersistence(opts) {
|
|
|
67917
67964
|
chmodSync(filePath, 384);
|
|
67918
67965
|
} catch (err) {
|
|
67919
67966
|
try {
|
|
67920
|
-
if (
|
|
67967
|
+
if (existsSync2(tmp))
|
|
67921
67968
|
unlinkSync(tmp);
|
|
67922
67969
|
} catch {
|
|
67923
67970
|
}
|
|
@@ -67926,7 +67973,7 @@ function createFileStatePersistence(opts) {
|
|
|
67926
67973
|
},
|
|
67927
67974
|
clear() {
|
|
67928
67975
|
try {
|
|
67929
|
-
if (
|
|
67976
|
+
if (existsSync2(filePath))
|
|
67930
67977
|
unlinkSync(filePath);
|
|
67931
67978
|
} catch {
|
|
67932
67979
|
}
|
|
@@ -67934,11 +67981,11 @@ function createFileStatePersistence(opts) {
|
|
|
67934
67981
|
};
|
|
67935
67982
|
}
|
|
67936
67983
|
function homeOf(env) {
|
|
67937
|
-
return readEnvVar("HOME", { env }) ??
|
|
67984
|
+
return readEnvVar("HOME", { env }) ?? homedir3();
|
|
67938
67985
|
}
|
|
67939
67986
|
function resolveStateDir(opts = {}) {
|
|
67940
67987
|
const env = opts.env;
|
|
67941
|
-
return
|
|
67988
|
+
return join4(readEnvVar("MCP_DATA_DIR", { env }) ?? homeOf(env), opts.subdir ?? "");
|
|
67942
67989
|
}
|
|
67943
67990
|
function resolveStateFile(opts) {
|
|
67944
67991
|
const override = opts.envVar !== void 0 ? readEnvVar(opts.envVar, { env: opts.env }) : void 0;
|
|
@@ -67946,13 +67993,13 @@ function resolveStateFile(opts) {
|
|
|
67946
67993
|
if (override === "~")
|
|
67947
67994
|
return homeOf(opts.env);
|
|
67948
67995
|
if (override.startsWith("~/"))
|
|
67949
|
-
return
|
|
67950
|
-
return
|
|
67996
|
+
return join4(homeOf(opts.env), override.slice(2));
|
|
67997
|
+
return resolve3(override);
|
|
67951
67998
|
}
|
|
67952
67999
|
const dirOpts = { env: opts.env };
|
|
67953
68000
|
if (opts.subdir !== void 0)
|
|
67954
68001
|
dirOpts.subdir = opts.subdir;
|
|
67955
|
-
return
|
|
68002
|
+
return join4(resolveStateDir(dirOpts), opts.fileName);
|
|
67956
68003
|
}
|
|
67957
68004
|
var TOKEN_REFRESH_SKEW_MS = 5 * 60 * 1e3;
|
|
67958
68005
|
function isPersistedCookieSession(raw) {
|
|
@@ -68242,7 +68289,8 @@ function reportCacheWriteFailure(err) {
|
|
|
68242
68289
|
}
|
|
68243
68290
|
|
|
68244
68291
|
// src/client.ts
|
|
68245
|
-
import { dirname as
|
|
68292
|
+
import { dirname as dirname3, isAbsolute as isAbsolute5, join as join5, resolve as resolve4 } from "path";
|
|
68293
|
+
import { homedir as homedir4 } from "os";
|
|
68246
68294
|
|
|
68247
68295
|
// src/session-login.ts
|
|
68248
68296
|
var USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36";
|
|
@@ -68340,6 +68388,8 @@ var CanvasClient = class {
|
|
|
68340
68388
|
* what keeps the path alive once the browser cookie lapses.
|
|
68341
68389
|
*/
|
|
68342
68390
|
refreshSession;
|
|
68391
|
+
/** Explicit download root; when unset, resolved per call (see {@link downloadRoot}). */
|
|
68392
|
+
outputDir;
|
|
68343
68393
|
auth;
|
|
68344
68394
|
/** Lazily-built shared refresh_token exchanger (oauth mode only). */
|
|
68345
68395
|
oauthRefresh = null;
|
|
@@ -68355,6 +68405,7 @@ var CanvasClient = class {
|
|
|
68355
68405
|
*/
|
|
68356
68406
|
constructor(account, opts = {}) {
|
|
68357
68407
|
this.account = account;
|
|
68408
|
+
this.outputDir = opts.outputDir;
|
|
68358
68409
|
this.sessionLoginFn = opts.sessionLogin ?? sessionLogin;
|
|
68359
68410
|
this.refreshSession = opts.refreshSession ?? null;
|
|
68360
68411
|
this.auth = new CookieSessionManager({
|
|
@@ -68396,33 +68447,85 @@ var CanvasClient = class {
|
|
|
68396
68447
|
}
|
|
68397
68448
|
return out;
|
|
68398
68449
|
}
|
|
68450
|
+
/**
|
|
68451
|
+
* Download a Canvas file to disk.
|
|
68452
|
+
*
|
|
68453
|
+
* Both inputs arrive from tool arguments, which a prompt injection in
|
|
68454
|
+
* classmate/teacher-authored Canvas content can steer, so both are pinned
|
|
68455
|
+
* BEFORE any credential is attached:
|
|
68456
|
+
* - `path` must resolve to the configured Canvas origin (https, same host —
|
|
68457
|
+
* the `https://canvas@evil/` userinfo trick parses to a foreign host and is
|
|
68458
|
+
* refused) and name a `/files/<id>` resource. Only the initial request is
|
|
68459
|
+
* pinned: Canvas legitimately redirects to its file store, and fetch
|
|
68460
|
+
* strips Authorization/Cookie on a cross-origin redirect.
|
|
68461
|
+
* - `destinationPath` must lie inside the download root (see
|
|
68462
|
+
* {@link downloadRoot}), checked through symlinks; a relative path is
|
|
68463
|
+
* resolved against that root.
|
|
68464
|
+
*/
|
|
68399
68465
|
async download(path, destinationPath, opts = {}) {
|
|
68466
|
+
const url4 = this.pinDownloadUrl(path);
|
|
68467
|
+
const dest = this.confineDestination(destinationPath);
|
|
68400
68468
|
let destStat = null;
|
|
68401
68469
|
try {
|
|
68402
|
-
destStat = await
|
|
68470
|
+
destStat = await lstat2(dest);
|
|
68403
68471
|
} catch {
|
|
68404
68472
|
}
|
|
68473
|
+
if (destStat?.isSymbolicLink()) throw new InvalidPathError(destinationPath, "is a symlink");
|
|
68405
68474
|
if (destStat?.isDirectory()) throw new InvalidPathError(destinationPath);
|
|
68406
68475
|
if (destStat && !opts.overwrite) throw new FileExistsError(destinationPath);
|
|
68407
|
-
const parent =
|
|
68476
|
+
const parent = dirname3(dest);
|
|
68408
68477
|
try {
|
|
68409
68478
|
await stat(parent);
|
|
68410
68479
|
} catch {
|
|
68411
68480
|
throw new ParentDirectoryMissingError(parent);
|
|
68412
68481
|
}
|
|
68413
|
-
const url4 = /^https?:\/\//i.test(path) ? path : `${this.account.baseUrl}${path}`;
|
|
68414
68482
|
const res = await this.authedFetch(url4, {});
|
|
68415
68483
|
if (res.status === 401) throw new TokenExpiredError(this.account.mode);
|
|
68416
68484
|
if (res.status === 404) throw new Error(`Canvas download 404 for ${path}`);
|
|
68417
68485
|
if (!res.ok) throw new Error(`Canvas download ${res.status} for ${path}`);
|
|
68418
68486
|
const buf = new Uint8Array(await res.arrayBuffer());
|
|
68419
|
-
await
|
|
68487
|
+
await writeConfined(dest, destinationPath, buf, opts.overwrite === true);
|
|
68420
68488
|
return {
|
|
68421
|
-
path:
|
|
68489
|
+
path: dest,
|
|
68422
68490
|
bytes: buf.byteLength,
|
|
68423
68491
|
contentType: res.headers.get("content-type") ?? "application/octet-stream"
|
|
68424
68492
|
};
|
|
68425
68493
|
}
|
|
68494
|
+
/**
|
|
68495
|
+
* The directory downloads are confined to: the constructor's `outputDir`,
|
|
68496
|
+
* else `CANVAS_OUTPUT_DIR`, else `~/Downloads`. Read per call so an env
|
|
68497
|
+
* change needs no client rebuild.
|
|
68498
|
+
*/
|
|
68499
|
+
downloadRoot() {
|
|
68500
|
+
return expandPath(this.outputDir ?? readEnvVar("CANVAS_OUTPUT_DIR") ?? join5(homedir4(), "Downloads"));
|
|
68501
|
+
}
|
|
68502
|
+
confineDestination(destinationPath) {
|
|
68503
|
+
const root = this.downloadRoot();
|
|
68504
|
+
const candidate = destinationPath.startsWith("~") || isAbsolute5(destinationPath) ? expandPath(destinationPath) : resolve4(root, destinationPath);
|
|
68505
|
+
try {
|
|
68506
|
+
assertPathWithinRoots(candidate, [root]);
|
|
68507
|
+
} catch {
|
|
68508
|
+
throw new DownloadDestinationRejectedError(destinationPath, root);
|
|
68509
|
+
}
|
|
68510
|
+
return candidate;
|
|
68511
|
+
}
|
|
68512
|
+
pinDownloadUrl(path) {
|
|
68513
|
+
const raw = /^[a-z][a-z0-9+.-]*:/i.test(path) ? path : `${this.account.baseUrl}${path}`;
|
|
68514
|
+
let parsed;
|
|
68515
|
+
try {
|
|
68516
|
+
parsed = new URL(raw);
|
|
68517
|
+
} catch {
|
|
68518
|
+
throw new DownloadUrlRejectedError(path, "not a valid URL");
|
|
68519
|
+
}
|
|
68520
|
+
const origin = new URL(this.account.baseUrl).origin;
|
|
68521
|
+
if (parsed.protocol !== "https:" || parsed.origin !== origin || parsed.username || parsed.password) {
|
|
68522
|
+
throw new DownloadUrlRejectedError(path, `must be an https URL on ${origin}`);
|
|
68523
|
+
}
|
|
68524
|
+
if (!/\/files\/[^/]+/.test(parsed.pathname)) {
|
|
68525
|
+
throw new DownloadUrlRejectedError(path, "must name a Canvas file (a /files/<id> path)");
|
|
68526
|
+
}
|
|
68527
|
+
return parsed.href;
|
|
68528
|
+
}
|
|
68426
68529
|
async doRawRequest(path, opts) {
|
|
68427
68530
|
const url4 = /^https?:\/\//i.test(path) ? path : `${this.account.baseUrl}${path}`;
|
|
68428
68531
|
const accept = opts.responseType === "text" ? "text/html, text/plain, */*" : "application/json+canvas-string-ids, application/json";
|
|
@@ -68535,8 +68638,8 @@ function parseJsonBody2(text) {
|
|
|
68535
68638
|
}
|
|
68536
68639
|
function injectPerPage(pathOrUrl, perPage) {
|
|
68537
68640
|
if (/[?&]per_page=/.test(pathOrUrl)) return pathOrUrl;
|
|
68538
|
-
const
|
|
68539
|
-
return `${pathOrUrl}${
|
|
68641
|
+
const sep2 = pathOrUrl.includes("?") ? "&" : "?";
|
|
68642
|
+
return `${pathOrUrl}${sep2}per_page=${perPage}`;
|
|
68540
68643
|
}
|
|
68541
68644
|
var TokenExpiredError = class extends Error {
|
|
68542
68645
|
constructor(mode, detail) {
|
|
@@ -68557,9 +68660,48 @@ var CanvasUnreachableError = class extends Error {
|
|
|
68557
68660
|
}
|
|
68558
68661
|
status;
|
|
68559
68662
|
};
|
|
68663
|
+
var DownloadUrlRejectedError = class extends Error {
|
|
68664
|
+
constructor(url4, reason) {
|
|
68665
|
+
super(`DownloadUrlRejected: ${reason}; use the \`url\` from canvas_list_course_files. Got: ${url4}`);
|
|
68666
|
+
this.url = url4;
|
|
68667
|
+
this.name = "DownloadUrlRejectedError";
|
|
68668
|
+
}
|
|
68669
|
+
url;
|
|
68670
|
+
};
|
|
68671
|
+
var DownloadDestinationRejectedError = class extends Error {
|
|
68672
|
+
constructor(path, root) {
|
|
68673
|
+
super(
|
|
68674
|
+
`DownloadDestinationRejected: destinationPath must be inside ${root} (set CANVAS_OUTPUT_DIR to change it). Got: ${path}`
|
|
68675
|
+
);
|
|
68676
|
+
this.path = path;
|
|
68677
|
+
this.root = root;
|
|
68678
|
+
this.name = "DownloadDestinationRejectedError";
|
|
68679
|
+
}
|
|
68680
|
+
path;
|
|
68681
|
+
root;
|
|
68682
|
+
};
|
|
68683
|
+
async function writeConfined(dest, requested, buf, overwrite) {
|
|
68684
|
+
const { O_WRONLY, O_CREAT, O_TRUNC, O_EXCL, O_NOFOLLOW } = fsConstants;
|
|
68685
|
+
const flags = O_WRONLY | O_CREAT | O_NOFOLLOW | (overwrite ? O_TRUNC : O_EXCL);
|
|
68686
|
+
let handle;
|
|
68687
|
+
try {
|
|
68688
|
+
handle = await open2(dest, flags, 384);
|
|
68689
|
+
} catch (e) {
|
|
68690
|
+
const code = e.code;
|
|
68691
|
+
if (code === "ELOOP") throw new InvalidPathError(requested, "is a symlink");
|
|
68692
|
+
if (code === "EEXIST") throw new FileExistsError(requested);
|
|
68693
|
+
if (code === "EISDIR") throw new InvalidPathError(requested);
|
|
68694
|
+
throw e;
|
|
68695
|
+
}
|
|
68696
|
+
try {
|
|
68697
|
+
await handle.writeFile(buf);
|
|
68698
|
+
} finally {
|
|
68699
|
+
await handle.close();
|
|
68700
|
+
}
|
|
68701
|
+
}
|
|
68560
68702
|
var InvalidPathError = class extends Error {
|
|
68561
|
-
constructor(path) {
|
|
68562
|
-
super(`InvalidPath: destinationPath
|
|
68703
|
+
constructor(path, reason = "must be a filename, not a directory") {
|
|
68704
|
+
super(`InvalidPath: destinationPath ${reason}: ${path}`);
|
|
68563
68705
|
this.path = path;
|
|
68564
68706
|
this.name = "InvalidPathError";
|
|
68565
68707
|
}
|
|
@@ -68920,12 +69062,13 @@ function registerConversationTools(server, client) {
|
|
|
68920
69062
|
return viewResponse(args.view, data);
|
|
68921
69063
|
});
|
|
68922
69064
|
server.registerTool("canvas_get_conversation", {
|
|
68923
|
-
description: "Get a full Canvas conversation thread with all messages.",
|
|
69065
|
+
description: "Get a full Canvas conversation thread with all messages. Read-only: does not mark the conversation as read.",
|
|
68924
69066
|
annotations: { readOnlyHint: true },
|
|
68925
69067
|
inputSchema: getArgs3
|
|
68926
69068
|
}, async (rawArgs) => {
|
|
68927
69069
|
const args = getArgs3.parse(rawArgs);
|
|
68928
69070
|
const path = buildPath(`/api/v1/conversations/${encodeURIComponent(args.id)}`, {
|
|
69071
|
+
auto_mark_as_read: false,
|
|
68929
69072
|
"include[]": ["participant_avatars"]
|
|
68930
69073
|
});
|
|
68931
69074
|
const data = await client.request(path);
|
|
@@ -68963,8 +69106,8 @@ var listArgs4 = external_exports.object({
|
|
|
68963
69106
|
contentTypes: external_exports.array(external_exports.string()).optional()
|
|
68964
69107
|
});
|
|
68965
69108
|
var downloadArgs = external_exports.object({
|
|
68966
|
-
url: external_exports.string().describe("The url field returned by canvas_list_course_files (
|
|
68967
|
-
destinationPath: external_exports.string().describe("
|
|
69109
|
+
url: external_exports.string().describe("The url field returned by canvas_list_course_files (an https /files/ URL on the configured Canvas host; anything else is refused)."),
|
|
69110
|
+
destinationPath: external_exports.string().describe("Where to write the file. Must be inside the download directory (CANVAS_OUTPUT_DIR, default ~/Downloads); a relative path is resolved against it."),
|
|
68968
69111
|
overwrite: external_exports.boolean().optional()
|
|
68969
69112
|
});
|
|
68970
69113
|
function registerFileTools(server, client) {
|
|
@@ -68982,8 +69125,8 @@ function registerFileTools(server, client) {
|
|
|
68982
69125
|
return textContent(data);
|
|
68983
69126
|
});
|
|
68984
69127
|
server.registerTool("canvas_download_file", {
|
|
68985
|
-
description: "Download a Canvas file to disk. `url` is the absolute URL from canvas_list_course_files; `destinationPath` is required.",
|
|
68986
|
-
annotations: { destructiveHint: true },
|
|
69128
|
+
description: "Download a Canvas file to disk. `url` is the absolute URL from canvas_list_course_files (only file URLs on the configured Canvas host are accepted); `destinationPath` is required and must be inside the download directory (CANVAS_OUTPUT_DIR, default ~/Downloads).",
|
|
69129
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
68987
69130
|
inputSchema: downloadArgs
|
|
68988
69131
|
}, async (rawArgs) => {
|
|
68989
69132
|
const args = downloadArgs.parse(rawArgs);
|
|
@@ -68995,8 +69138,8 @@ function registerFileTools(server, client) {
|
|
|
68995
69138
|
}
|
|
68996
69139
|
|
|
68997
69140
|
// src/index.ts
|
|
68998
|
-
var __dirname =
|
|
68999
|
-
await loadDotenvSafely({ path:
|
|
69141
|
+
var __dirname = dirname4(fileURLToPath(import.meta.url));
|
|
69142
|
+
await loadDotenvSafely({ path: join6(__dirname, "..", ".env"), override: false });
|
|
69000
69143
|
var resolved = null;
|
|
69001
69144
|
var configError = null;
|
|
69002
69145
|
try {
|
|
@@ -69006,7 +69149,7 @@ try {
|
|
|
69006
69149
|
}
|
|
69007
69150
|
await runMcp({
|
|
69008
69151
|
name: "canvas",
|
|
69009
|
-
version: "2.1.
|
|
69152
|
+
version: "2.1.4",
|
|
69010
69153
|
// x-release-please-version
|
|
69011
69154
|
tools: [
|
|
69012
69155
|
// Outside the `if (resolved)` on purpose: an unconfigured server must
|