gogcli-mcp-classroom 4.2.5 → 4.4.0
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/dist/index.js +903 -139
- package/manifest.json +9 -2
- package/package.json +2 -2
- package/server.json +2 -2
- package/src/tools/classroom-extra.ts +45 -22
- package/tests/tools/classroom-extra.test.ts +76 -24
- package/vitest.config.ts +5 -0
package/dist/index.js
CHANGED
|
@@ -37,6 +37,33 @@ var storage2 = new AsyncLocalStorage2();
|
|
|
37
37
|
function withCallerCapabilities(capabilities, fn) {
|
|
38
38
|
return capabilities ? storage2.run(capabilities, fn) : fn();
|
|
39
39
|
}
|
|
40
|
+
function currentCallerCapabilities() {
|
|
41
|
+
return storage2.getStore();
|
|
42
|
+
}
|
|
43
|
+
var ENVELOPE_CAPABILITIES_KEY = "io.modelcontextprotocol/clientCapabilities";
|
|
44
|
+
var ELICITATION_MODES = ["form", "url"];
|
|
45
|
+
function isRecord(value) {
|
|
46
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
47
|
+
}
|
|
48
|
+
function callerCapabilities(ctx) {
|
|
49
|
+
const envelope = isRecord(ctx) && isRecord(ctx.mcpReq) ? ctx.mcpReq.envelope : void 0;
|
|
50
|
+
if (isRecord(envelope)) {
|
|
51
|
+
const declared = envelope[ENVELOPE_CAPABILITIES_KEY];
|
|
52
|
+
if (isRecord(declared))
|
|
53
|
+
return declared;
|
|
54
|
+
}
|
|
55
|
+
return currentCallerCapabilities();
|
|
56
|
+
}
|
|
57
|
+
function callerAcceptsFormElicitation(ctx) {
|
|
58
|
+
const capabilities = callerCapabilities(ctx);
|
|
59
|
+
if (!capabilities)
|
|
60
|
+
return void 0;
|
|
61
|
+
const elicitation = capabilities.elicitation;
|
|
62
|
+
if (!isRecord(elicitation))
|
|
63
|
+
return false;
|
|
64
|
+
const namedModes = ELICITATION_MODES.filter((mode) => mode in elicitation);
|
|
65
|
+
return namedModes.length === 0 || namedModes.includes("form");
|
|
66
|
+
}
|
|
40
67
|
|
|
41
68
|
// ../../node_modules/@modelcontextprotocol/server/dist/chunk-Br0eD_fh.mjs
|
|
42
69
|
var __create = Object.create;
|
|
@@ -2092,8 +2119,8 @@ function emoji() {
|
|
|
2092
2119
|
}
|
|
2093
2120
|
var ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
|
|
2094
2121
|
var ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
|
|
2095
|
-
var mac = (
|
|
2096
|
-
const escapedDelim = escapeRegex(
|
|
2122
|
+
var mac = (delimiter3) => {
|
|
2123
|
+
const escapedDelim = escapeRegex(delimiter3 ?? ":");
|
|
2097
2124
|
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
|
|
2098
2125
|
};
|
|
2099
2126
|
var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
|
|
@@ -5121,7 +5148,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
|
|
|
5121
5148
|
var NONE = 0;
|
|
5122
5149
|
var ASSUMED = 1;
|
|
5123
5150
|
var PROVEN = 2;
|
|
5124
|
-
function isRecursive(inst, stack,
|
|
5151
|
+
function isRecursive(inst, stack, resolve4) {
|
|
5125
5152
|
const cached2 = recursive.get(inst);
|
|
5126
5153
|
if (cached2 !== void 0)
|
|
5127
5154
|
return cached2 ? PROVEN : NONE;
|
|
@@ -5131,7 +5158,7 @@ function isRecursive(inst, stack, resolve) {
|
|
|
5131
5158
|
let result = NONE;
|
|
5132
5159
|
const check2 = (child) => {
|
|
5133
5160
|
if (result !== PROVEN && child?._zod) {
|
|
5134
|
-
const answer = isRecursive(child, stack,
|
|
5161
|
+
const answer = isRecursive(child, stack, resolve4);
|
|
5135
5162
|
if (answer > result)
|
|
5136
5163
|
result = answer;
|
|
5137
5164
|
}
|
|
@@ -5142,7 +5169,7 @@ function isRecursive(inst, stack, resolve) {
|
|
|
5142
5169
|
const desc = Object.getOwnPropertyDescriptor(sh, key);
|
|
5143
5170
|
if (spread && !desc.enumerable)
|
|
5144
5171
|
continue;
|
|
5145
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
5172
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve4) : NONE;
|
|
5146
5173
|
if (child > answer)
|
|
5147
5174
|
answer = child;
|
|
5148
5175
|
}
|
|
@@ -5206,7 +5233,7 @@ function isRecursive(inst, stack, resolve) {
|
|
|
5206
5233
|
break;
|
|
5207
5234
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
5208
5235
|
case "lazy": {
|
|
5209
|
-
const inner = def._cachedInner ?? (
|
|
5236
|
+
const inner = def._cachedInner ?? (resolve4 ? inst._zod.innerType : void 0);
|
|
5210
5237
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
5211
5238
|
break;
|
|
5212
5239
|
}
|
|
@@ -24101,19 +24128,50 @@ var inputRequired = Object.assign(buildInputRequired, {
|
|
|
24101
24128
|
return { method: "roots/list" };
|
|
24102
24129
|
}
|
|
24103
24130
|
});
|
|
24131
|
+
function acceptedContent(responses, key, schema) {
|
|
24132
|
+
const view = inputResponse(responses, key);
|
|
24133
|
+
if (view.kind !== "elicit" || view.action !== "accept" || view.content === void 0) return void 0;
|
|
24134
|
+
if (schema === void 0) return view.content;
|
|
24135
|
+
const outcome = schema["~standard"].validate(view.content);
|
|
24136
|
+
if (outcome instanceof Promise) throw new TypeError("acceptedContent(responses, key, schema) requires a synchronously-validating schema");
|
|
24137
|
+
return outcome.issues === void 0 ? outcome.value : void 0;
|
|
24138
|
+
}
|
|
24139
|
+
function inputResponse(responses, key) {
|
|
24140
|
+
if (responses === void 0 || typeof responses !== "object" || responses === null) return { kind: "missing" };
|
|
24141
|
+
const entry = responses[key];
|
|
24142
|
+
if (entry === null || typeof entry !== "object" || Array.isArray(entry)) return { kind: "missing" };
|
|
24143
|
+
const candidate = entry;
|
|
24144
|
+
if (candidate["action"] === "accept" || candidate["action"] === "decline" || candidate["action"] === "cancel") {
|
|
24145
|
+
const content = candidate["content"];
|
|
24146
|
+
return {
|
|
24147
|
+
kind: "elicit",
|
|
24148
|
+
action: candidate["action"],
|
|
24149
|
+
...content !== null && typeof content === "object" && !Array.isArray(content) && { content }
|
|
24150
|
+
};
|
|
24151
|
+
}
|
|
24152
|
+
if (Array.isArray(candidate["roots"])) return {
|
|
24153
|
+
kind: "roots",
|
|
24154
|
+
roots: candidate["roots"]
|
|
24155
|
+
};
|
|
24156
|
+
if (typeof candidate["role"] === "string" && candidate["content"] !== void 0) return {
|
|
24157
|
+
kind: "sampling",
|
|
24158
|
+
result: candidate
|
|
24159
|
+
};
|
|
24160
|
+
return { kind: "missing" };
|
|
24161
|
+
}
|
|
24104
24162
|
var REQUEST_STATE_ONLY_LEG_PACING_MS = 250;
|
|
24105
24163
|
function inputRequiredRoundsExceededMessage(method, maxRounds) {
|
|
24106
24164
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
24107
24165
|
}
|
|
24108
24166
|
function sleep(ms, signal) {
|
|
24109
|
-
return new Promise((
|
|
24167
|
+
return new Promise((resolve4, reject) => {
|
|
24110
24168
|
if (signal?.aborted) {
|
|
24111
24169
|
reject(signal.reason instanceof SdkError ? signal.reason : new SdkError(SdkErrorCode.RequestTimeout, String(signal.reason)));
|
|
24112
24170
|
return;
|
|
24113
24171
|
}
|
|
24114
24172
|
const timer = setTimeout(() => {
|
|
24115
24173
|
signal?.removeEventListener("abort", onAbort);
|
|
24116
|
-
|
|
24174
|
+
resolve4();
|
|
24117
24175
|
}, ms);
|
|
24118
24176
|
const onAbort = () => {
|
|
24119
24177
|
clearTimeout(timer);
|
|
@@ -24907,7 +24965,7 @@ var Protocol = class {
|
|
|
24907
24965
|
const flowStartedAt = Date.now();
|
|
24908
24966
|
let onAbort;
|
|
24909
24967
|
let cleanupMessageId;
|
|
24910
|
-
return new Promise((
|
|
24968
|
+
return new Promise((resolve4, reject) => {
|
|
24911
24969
|
const earlyReject = (error62) => {
|
|
24912
24970
|
reject(error62);
|
|
24913
24971
|
};
|
|
@@ -24975,7 +25033,7 @@ var Protocol = class {
|
|
|
24975
25033
|
}
|
|
24976
25034
|
if (decoded.kind === "invalid") return reject(decoded.error);
|
|
24977
25035
|
if (decoded.kind === "input_required") {
|
|
24978
|
-
if (options?.allowInputRequired === true) return
|
|
25036
|
+
if (options?.allowInputRequired === true) return resolve4(manualInputRequiredValue(decoded));
|
|
24979
25037
|
const flow = {
|
|
24980
25038
|
codec: codec2,
|
|
24981
25039
|
request,
|
|
@@ -24987,11 +25045,11 @@ var Protocol = class {
|
|
|
24987
25045
|
params
|
|
24988
25046
|
}, resultSchema, legOptions)
|
|
24989
25047
|
};
|
|
24990
|
-
return
|
|
25048
|
+
return resolve4(this._resolveNonCompleteResult(decoded, flow));
|
|
24991
25049
|
}
|
|
24992
25050
|
const result = decoded.result;
|
|
24993
25051
|
validateStandardSchema(resultSchema, result).then((parseResult) => {
|
|
24994
|
-
if (parseResult.success)
|
|
25052
|
+
if (parseResult.success) resolve4(parseResult.data);
|
|
24995
25053
|
else reject(new SdkError(SdkErrorCode.InvalidResult, `Invalid result for ${request.method}: ${parseResult.error}`));
|
|
24996
25054
|
}, reject);
|
|
24997
25055
|
});
|
|
@@ -27903,7 +27961,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
27903
27961
|
ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref);
|
|
27904
27962
|
const schOrFunc = root.refs[ref];
|
|
27905
27963
|
if (schOrFunc) return schOrFunc;
|
|
27906
|
-
let _sch =
|
|
27964
|
+
let _sch = resolve4.call(this, root, ref);
|
|
27907
27965
|
if (_sch === void 0) {
|
|
27908
27966
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
27909
27967
|
const { schemaId } = this.opts;
|
|
@@ -27929,7 +27987,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
27929
27987
|
function sameSchemaEnv(s1, s2) {
|
|
27930
27988
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
27931
27989
|
}
|
|
27932
|
-
function
|
|
27990
|
+
function resolve4(root, ref) {
|
|
27933
27991
|
let sch;
|
|
27934
27992
|
while (typeof (sch = this.refs[ref]) == "string") ref = sch;
|
|
27935
27993
|
return sch || this.schemas[ref] || resolveSchema.call(this, root, ref);
|
|
@@ -28379,47 +28437,47 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
28379
28437
|
else if (typeof uri === "object") uri = parse3(serialize(uri, options), options);
|
|
28380
28438
|
return uri;
|
|
28381
28439
|
}
|
|
28382
|
-
function
|
|
28440
|
+
function resolve4(baseURI, relativeURI, options) {
|
|
28383
28441
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
28384
28442
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
28385
28443
|
schemelessOptions.skipEscape = true;
|
|
28386
28444
|
return serialize(resolved, schemelessOptions);
|
|
28387
28445
|
}
|
|
28388
|
-
function resolveComponent(base,
|
|
28446
|
+
function resolveComponent(base, relative3, options, skipNormalization) {
|
|
28389
28447
|
const target = {};
|
|
28390
28448
|
if (!skipNormalization) {
|
|
28391
28449
|
base = parse3(serialize(base, options), options);
|
|
28392
|
-
|
|
28450
|
+
relative3 = parse3(serialize(relative3, options), options);
|
|
28393
28451
|
}
|
|
28394
28452
|
options = options || {};
|
|
28395
|
-
if (!options.tolerant &&
|
|
28396
|
-
target.scheme =
|
|
28397
|
-
target.userinfo =
|
|
28398
|
-
target.host =
|
|
28399
|
-
target.port =
|
|
28400
|
-
target.path = removeDotSegments(
|
|
28401
|
-
target.query =
|
|
28453
|
+
if (!options.tolerant && relative3.scheme) {
|
|
28454
|
+
target.scheme = relative3.scheme;
|
|
28455
|
+
target.userinfo = relative3.userinfo;
|
|
28456
|
+
target.host = relative3.host;
|
|
28457
|
+
target.port = relative3.port;
|
|
28458
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
28459
|
+
target.query = relative3.query;
|
|
28402
28460
|
} else {
|
|
28403
|
-
if (
|
|
28404
|
-
target.userinfo =
|
|
28405
|
-
target.host =
|
|
28406
|
-
target.port =
|
|
28407
|
-
target.path = removeDotSegments(
|
|
28408
|
-
target.query =
|
|
28461
|
+
if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
|
|
28462
|
+
target.userinfo = relative3.userinfo;
|
|
28463
|
+
target.host = relative3.host;
|
|
28464
|
+
target.port = relative3.port;
|
|
28465
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
28466
|
+
target.query = relative3.query;
|
|
28409
28467
|
} else {
|
|
28410
|
-
if (!
|
|
28468
|
+
if (!relative3.path) {
|
|
28411
28469
|
target.path = base.path;
|
|
28412
|
-
if (
|
|
28470
|
+
if (relative3.query !== void 0) target.query = relative3.query;
|
|
28413
28471
|
else target.query = base.query;
|
|
28414
28472
|
} else {
|
|
28415
|
-
if (
|
|
28473
|
+
if (relative3.path[0] === "/") target.path = removeDotSegments(relative3.path);
|
|
28416
28474
|
else {
|
|
28417
|
-
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) target.path = "/" +
|
|
28418
|
-
else if (!base.path) target.path =
|
|
28419
|
-
else target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
28475
|
+
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) target.path = "/" + relative3.path;
|
|
28476
|
+
else if (!base.path) target.path = relative3.path;
|
|
28477
|
+
else target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
|
|
28420
28478
|
target.path = removeDotSegments(target.path);
|
|
28421
28479
|
}
|
|
28422
|
-
target.query =
|
|
28480
|
+
target.query = relative3.query;
|
|
28423
28481
|
}
|
|
28424
28482
|
target.userinfo = base.userinfo;
|
|
28425
28483
|
target.host = base.host;
|
|
@@ -28427,7 +28485,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
28427
28485
|
}
|
|
28428
28486
|
target.scheme = base.scheme;
|
|
28429
28487
|
}
|
|
28430
|
-
target.fragment =
|
|
28488
|
+
target.fragment = relative3.fragment;
|
|
28431
28489
|
return target;
|
|
28432
28490
|
}
|
|
28433
28491
|
function equal(uriA, uriB, options) {
|
|
@@ -28553,7 +28611,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
28553
28611
|
const fastUri = {
|
|
28554
28612
|
SCHEMES,
|
|
28555
28613
|
normalize,
|
|
28556
|
-
resolve,
|
|
28614
|
+
resolve: resolve4,
|
|
28557
28615
|
resolveComponent,
|
|
28558
28616
|
equal,
|
|
28559
28617
|
serialize,
|
|
@@ -29335,14 +29393,14 @@ var require_ucs2length = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
29335
29393
|
function ucs2length(str) {
|
|
29336
29394
|
const len = str.length;
|
|
29337
29395
|
let length = 0;
|
|
29338
|
-
let
|
|
29396
|
+
let pos2 = 0;
|
|
29339
29397
|
let value;
|
|
29340
|
-
while (
|
|
29398
|
+
while (pos2 < len) {
|
|
29341
29399
|
length++;
|
|
29342
|
-
value = str.charCodeAt(
|
|
29343
|
-
if (value >= 55296 && value <= 56319 &&
|
|
29344
|
-
value = str.charCodeAt(
|
|
29345
|
-
if ((value & 64512) === 56320)
|
|
29400
|
+
value = str.charCodeAt(pos2++);
|
|
29401
|
+
if (value >= 55296 && value <= 56319 && pos2 < len) {
|
|
29402
|
+
value = str.charCodeAt(pos2);
|
|
29403
|
+
if ((value & 64512) === 56320) pos2++;
|
|
29346
29404
|
}
|
|
29347
29405
|
}
|
|
29348
29406
|
return length;
|
|
@@ -33926,7 +33984,7 @@ var StdioServerTransport = class {
|
|
|
33926
33984
|
}
|
|
33927
33985
|
send(message) {
|
|
33928
33986
|
if (this._closed) return Promise.reject(/* @__PURE__ */ new Error("StdioServerTransport is closed"));
|
|
33929
|
-
return new Promise((
|
|
33987
|
+
return new Promise((resolve4, reject) => {
|
|
33930
33988
|
const json2 = serializeMessage(message);
|
|
33931
33989
|
let settled = false;
|
|
33932
33990
|
const onError = (error62) => {
|
|
@@ -33941,14 +33999,14 @@ var StdioServerTransport = class {
|
|
|
33941
33999
|
settled = true;
|
|
33942
34000
|
this._stdout.off("error", onError);
|
|
33943
34001
|
this._stdout.off("drain", onDrain);
|
|
33944
|
-
|
|
34002
|
+
resolve4();
|
|
33945
34003
|
};
|
|
33946
34004
|
this._stdout.once("error", onError);
|
|
33947
34005
|
if (this._stdout.write(json2)) {
|
|
33948
34006
|
if (settled) return;
|
|
33949
34007
|
settled = true;
|
|
33950
34008
|
this._stdout.off("error", onError);
|
|
33951
|
-
|
|
34009
|
+
resolve4();
|
|
33952
34010
|
} else if (!settled) this._stdout.once("drain", onDrain);
|
|
33953
34011
|
});
|
|
33954
34012
|
}
|
|
@@ -34002,14 +34060,14 @@ var StdioConnectionChannel = class {
|
|
|
34002
34060
|
*/
|
|
34003
34061
|
async whenRequestsAnswered(timeoutMs) {
|
|
34004
34062
|
if (this._closed || this._pendingRequests.size === 0) return true;
|
|
34005
|
-
return await new Promise((
|
|
34063
|
+
return await new Promise((resolve4) => {
|
|
34006
34064
|
const waiter = () => {
|
|
34007
34065
|
clearTimeout(timer);
|
|
34008
|
-
|
|
34066
|
+
resolve4(true);
|
|
34009
34067
|
};
|
|
34010
34068
|
const timer = setTimeout(() => {
|
|
34011
34069
|
this._drainWaiters = this._drainWaiters.filter((pending) => pending !== waiter);
|
|
34012
|
-
|
|
34070
|
+
resolve4(false);
|
|
34013
34071
|
}, timeoutMs);
|
|
34014
34072
|
this._drainWaiters.push(waiter);
|
|
34015
34073
|
});
|
|
@@ -34148,14 +34206,14 @@ function serveStdio(factory, options = {}) {
|
|
|
34148
34206
|
return false;
|
|
34149
34207
|
};
|
|
34150
34208
|
const answerLegacyRejection = (request, reason, requestedVersion) => {
|
|
34151
|
-
const
|
|
34209
|
+
const rejection3 = modernOnlyStrictRejection({
|
|
34152
34210
|
kind: "legacy",
|
|
34153
34211
|
reason,
|
|
34154
34212
|
...requestedVersion !== void 0 && { requestedVersion }
|
|
34155
34213
|
}, SUPPORTED_MODERN_PROTOCOL_VERSIONS);
|
|
34156
|
-
if (
|
|
34157
|
-
reportError(/* @__PURE__ */ new Error(`Rejected 2025-era request on a modern-only stdio connection (${
|
|
34158
|
-
return writeErrorResponse(request.id,
|
|
34214
|
+
if (rejection3 === void 0) return Promise.resolve();
|
|
34215
|
+
reportError(/* @__PURE__ */ new Error(`Rejected 2025-era request on a modern-only stdio connection (${rejection3.cell}): ${rejection3.message}`));
|
|
34216
|
+
return writeErrorResponse(request.id, rejection3.code, rejection3.message, rejection3.data);
|
|
34159
34217
|
};
|
|
34160
34218
|
const onInstanceClosed = (channel) => {
|
|
34161
34219
|
if (closing || channel === discarding) return;
|
|
@@ -34379,13 +34437,28 @@ var API_KEY_RE = new RegExp([
|
|
|
34379
34437
|
"whsec_[A-Za-z0-9]{16,}\\b"
|
|
34380
34438
|
// webhook signing secret (Stripe-style)
|
|
34381
34439
|
].map((p) => `\\b${p}`).join("|"), "g");
|
|
34382
|
-
var QUERY_SECRET_RE = /([?&](?:
|
|
34440
|
+
var QUERY_SECRET_RE = /([?&](?:(?:access|refresh|id|auth|session|csrf|xsrf)[_-]?token|client[_-]?secret|api[_-]?secret|api[_-]?key|password|passwd|signature|token|key|sig)=)[^&#\s"'<>`]+/gi;
|
|
34441
|
+
var OAUTH_CODE_RE = /([?&]code=)(?=[^&#\s"'<>`]{16,})[^&#\s"'<>`]+/gi;
|
|
34442
|
+
var FORM_PASSWORD_RE = /((?:^|[\s,;:"'(\[{])(?:password|passwd)=)[^&#\s"'<>`]+/gim;
|
|
34443
|
+
var HEADER_SECRET_RE = /(\b(?:x[-_][a-z0-9_-]*?(?:api[-_]?key|token|secret|auth)[a-z0-9_-]*|api[-_]?key)\s*:\s*)[^\s,;"'<>`]+/gi;
|
|
34383
34444
|
var AWS_SIGV4_RE = /([?&]X-Amz-(?:Signature|Security-Token|Credential)=)[^&#\s"'<>`]+/gi;
|
|
34384
|
-
var JSON_SECRET_KEYS =
|
|
34385
|
-
|
|
34386
|
-
|
|
34445
|
+
var JSON_SECRET_KEYS = [
|
|
34446
|
+
"(?:access|refresh|id|auth|session|bearer|csrf|xsrf)[_-]?token",
|
|
34447
|
+
"client[_-]?secret",
|
|
34448
|
+
"api[_-]?secret",
|
|
34449
|
+
"(?:x[_-])?api[_-]?key",
|
|
34450
|
+
"private[_-]?key",
|
|
34451
|
+
"x[_-][a-z0-9_-]*?(?:api[_-]?key|token|secret|auth)[a-z0-9_-]*",
|
|
34452
|
+
"(?:proxy-)?authorization",
|
|
34453
|
+
"password",
|
|
34454
|
+
"passwd",
|
|
34455
|
+
"secret",
|
|
34456
|
+
"token"
|
|
34457
|
+
].join("|");
|
|
34458
|
+
var JSON_SECRET_DQ_RE = new RegExp(`("(?:${JSON_SECRET_KEYS})"\\s*:\\s*")(?:[^"\\\\]|\\\\.)*(")`, "gi");
|
|
34459
|
+
var JSON_SECRET_SQ_RE = new RegExp(`('(?:${JSON_SECRET_KEYS})'\\s*:\\s*')(?:[^'\\\\]|\\\\.)*(')`, "gi");
|
|
34387
34460
|
function redactSecrets(text) {
|
|
34388
|
-
return text.replace(BEARER_RE, "$1[REDACTED]").replace(BASIC_AUTH_RE, "$1[REDACTED]").replace(SET_COOKIE_RE, "$1$2=[REDACTED]").replace(COOKIE_HEADER_RE, (_m, prefix, pairs) => `${prefix}${pairs.replace(/=[^;,\s]*/g, "=[REDACTED]")}`).replace(API_KEY_RE, "[REDACTED]").replace(QUERY_SECRET_RE, "$1[REDACTED]").replace(AWS_SIGV4_RE, "$1[REDACTED]").replace(JSON_SECRET_DQ_RE, "$1[REDACTED]$2").replace(JSON_SECRET_SQ_RE, "$1[REDACTED]$2").replace(JWT_RE, "[REDACTED]");
|
|
34461
|
+
return text.replace(BEARER_RE, "$1[REDACTED]").replace(BASIC_AUTH_RE, "$1[REDACTED]").replace(SET_COOKIE_RE, "$1$2=[REDACTED]").replace(COOKIE_HEADER_RE, (_m, prefix, pairs) => `${prefix}${pairs.replace(/=[^;,\s]*/g, "=[REDACTED]")}`).replace(API_KEY_RE, "[REDACTED]").replace(HEADER_SECRET_RE, "$1[REDACTED]").replace(QUERY_SECRET_RE, "$1[REDACTED]").replace(OAUTH_CODE_RE, "$1[REDACTED]").replace(FORM_PASSWORD_RE, "$1[REDACTED]").replace(AWS_SIGV4_RE, "$1[REDACTED]").replace(JSON_SECRET_DQ_RE, "$1[REDACTED]$2").replace(JSON_SECRET_SQ_RE, "$1[REDACTED]$2").replace(JWT_RE, "[REDACTED]");
|
|
34389
34462
|
}
|
|
34390
34463
|
|
|
34391
34464
|
// ../../node_modules/@chrischall/mcp-utils/dist/response/view.js
|
|
@@ -34443,6 +34516,11 @@ function walk(value, keep, drop) {
|
|
|
34443
34516
|
}
|
|
34444
34517
|
|
|
34445
34518
|
// ../../node_modules/@chrischall/mcp-utils/dist/response/index.js
|
|
34519
|
+
function textResult(data) {
|
|
34520
|
+
return {
|
|
34521
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
34522
|
+
};
|
|
34523
|
+
}
|
|
34446
34524
|
function rawTextResult(text) {
|
|
34447
34525
|
return { content: [{ type: "text", text }] };
|
|
34448
34526
|
}
|
|
@@ -34453,6 +34531,154 @@ function errorResult(message) {
|
|
|
34453
34531
|
};
|
|
34454
34532
|
}
|
|
34455
34533
|
|
|
34534
|
+
// ../../node_modules/@chrischall/mcp-utils/dist/server/confirmation.js
|
|
34535
|
+
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
34536
|
+
var DEFAULT_REQUEST_KEY = "confirmation";
|
|
34537
|
+
var DEFAULT_CONFIRMATION_LABEL = "Confirm this action should proceed.";
|
|
34538
|
+
var UNSUPPORTED_NOTE = "Nothing was done because this client cannot show a confirmation prompt (it declares no MCP elicitation capability), and this action is never taken without one";
|
|
34539
|
+
var STATE_PREFIX = "mcpu.confirm.v1.";
|
|
34540
|
+
function canonicalJson(value) {
|
|
34541
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
34542
|
+
return JSON.stringify(value);
|
|
34543
|
+
if (value === void 0)
|
|
34544
|
+
return "null";
|
|
34545
|
+
if (typeof value === "number")
|
|
34546
|
+
return Number.isFinite(value) ? JSON.stringify(value) : `{"$num":"${String(value)}"}`;
|
|
34547
|
+
if (typeof value === "bigint")
|
|
34548
|
+
return `{"$bigint":"${value.toString()}"}`;
|
|
34549
|
+
if (typeof value === "function" || typeof value === "symbol") {
|
|
34550
|
+
throw new TypeError(`requireConfirmation: cannot bind a ${typeof value} argument value.`);
|
|
34551
|
+
}
|
|
34552
|
+
if (Array.isArray(value))
|
|
34553
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
34554
|
+
if (value instanceof Date) {
|
|
34555
|
+
return `{"$date":${JSON.stringify(Number.isNaN(value.getTime()) ? "Invalid Date" : value.toISOString())}}`;
|
|
34556
|
+
}
|
|
34557
|
+
if (ArrayBuffer.isView(value)) {
|
|
34558
|
+
const bytes = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
34559
|
+
return `{"$bytes":${JSON.stringify(bytes.toString("base64"))}}`;
|
|
34560
|
+
}
|
|
34561
|
+
if (value instanceof ArrayBuffer)
|
|
34562
|
+
return `{"$bytes":${JSON.stringify(Buffer.from(value).toString("base64"))}}`;
|
|
34563
|
+
if (value instanceof Map) {
|
|
34564
|
+
const entries2 = [...value.entries()].map(([k, v]) => `[${canonicalJson(k)},${canonicalJson(v)}]`).sort();
|
|
34565
|
+
return `{"$map":[${entries2.join(",")}]}`;
|
|
34566
|
+
}
|
|
34567
|
+
if (value instanceof Set) {
|
|
34568
|
+
return `{"$set":[${[...value].map(canonicalJson).sort().join(",")}]}`;
|
|
34569
|
+
}
|
|
34570
|
+
const proto = Object.getPrototypeOf(value);
|
|
34571
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
34572
|
+
throw new TypeError("requireConfirmation: cannot bind a non-plain object argument value (class instance).");
|
|
34573
|
+
}
|
|
34574
|
+
const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
34575
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(escapeKey(k))}:${canonicalJson(v)}`).join(",")}}`;
|
|
34576
|
+
}
|
|
34577
|
+
function escapeKey(key) {
|
|
34578
|
+
return key.startsWith("$") ? `$${key}` : key;
|
|
34579
|
+
}
|
|
34580
|
+
function bindingKey(binding) {
|
|
34581
|
+
if (binding.ttlSeconds !== void 0 && !(Number.isFinite(binding.ttlSeconds) && binding.ttlSeconds > 0)) {
|
|
34582
|
+
throw new RangeError("requireConfirmation: binding.ttlSeconds must be a finite number greater than 0.");
|
|
34583
|
+
}
|
|
34584
|
+
const key = typeof binding.key === "string" ? Buffer.from(binding.key, "utf8") : Buffer.from(binding.key);
|
|
34585
|
+
if (key.length < 32) {
|
|
34586
|
+
throw new RangeError("requireConfirmation: binding.key must be at least 32 bytes.");
|
|
34587
|
+
}
|
|
34588
|
+
return key;
|
|
34589
|
+
}
|
|
34590
|
+
function commitment(action, args) {
|
|
34591
|
+
return createHash("sha256").update(`${action}\0${canonicalJson(args)}`).digest("base64url");
|
|
34592
|
+
}
|
|
34593
|
+
function mintState(key, action, binding) {
|
|
34594
|
+
const exp = Math.floor(Date.now() / 1e3) + (binding.ttlSeconds ?? 600);
|
|
34595
|
+
const body = Buffer.from(JSON.stringify({ c: commitment(action, binding.args), exp })).toString("base64url");
|
|
34596
|
+
const mac3 = createHmac("sha256", key).update(`${STATE_PREFIX}${body}`).digest("base64url");
|
|
34597
|
+
return `${STATE_PREFIX}${body}.${mac3}`;
|
|
34598
|
+
}
|
|
34599
|
+
function verifyState(key, action, binding, state) {
|
|
34600
|
+
if (typeof state !== "string" || !state.startsWith(STATE_PREFIX))
|
|
34601
|
+
return false;
|
|
34602
|
+
const rest = state.slice(STATE_PREFIX.length);
|
|
34603
|
+
const dot = rest.indexOf(".");
|
|
34604
|
+
if (dot < 0)
|
|
34605
|
+
return false;
|
|
34606
|
+
const body = rest.slice(0, dot);
|
|
34607
|
+
const mac3 = Buffer.from(rest.slice(dot + 1), "base64url");
|
|
34608
|
+
const expected = createHmac("sha256", key).update(`${STATE_PREFIX}${body}`).digest();
|
|
34609
|
+
if (mac3.length !== expected.length || !timingSafeEqual(mac3, expected))
|
|
34610
|
+
return false;
|
|
34611
|
+
let payload;
|
|
34612
|
+
try {
|
|
34613
|
+
payload = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
|
|
34614
|
+
} catch {
|
|
34615
|
+
return false;
|
|
34616
|
+
}
|
|
34617
|
+
if (typeof payload.exp !== "number" || payload.exp * 1e3 <= Date.now())
|
|
34618
|
+
return false;
|
|
34619
|
+
const want = Buffer.from(commitment(action, binding.args));
|
|
34620
|
+
const got = Buffer.from(typeof payload.c === "string" ? payload.c : "");
|
|
34621
|
+
return got.length === want.length && timingSafeEqual(got, want);
|
|
34622
|
+
}
|
|
34623
|
+
function echoedState(ctx) {
|
|
34624
|
+
const accessor = ctx.mcpReq.requestState;
|
|
34625
|
+
return typeof accessor === "function" ? accessor() : void 0;
|
|
34626
|
+
}
|
|
34627
|
+
function requireConfirmation(ctx, options) {
|
|
34628
|
+
const requestKey = options.requestKey ?? DEFAULT_REQUEST_KEY;
|
|
34629
|
+
const key = options.binding ? bindingKey(options.binding) : void 0;
|
|
34630
|
+
const confirmationSchema = external_exports.object({
|
|
34631
|
+
confirmed: external_exports.boolean().describe(options.confirmationLabel ?? DEFAULT_CONFIRMATION_LABEL)
|
|
34632
|
+
});
|
|
34633
|
+
const ask = () => {
|
|
34634
|
+
const preview = {
|
|
34635
|
+
action: options.action,
|
|
34636
|
+
...options.details === void 0 ? {} : { details: options.details }
|
|
34637
|
+
};
|
|
34638
|
+
return inputRequired({
|
|
34639
|
+
inputRequests: {
|
|
34640
|
+
[requestKey]: inputRequired.elicit({
|
|
34641
|
+
message: `${options.message}
|
|
34642
|
+
${JSON.stringify(preview, null, 2)}`,
|
|
34643
|
+
requestedSchema: confirmationSchema
|
|
34644
|
+
})
|
|
34645
|
+
},
|
|
34646
|
+
...key && options.binding ? { requestState: mintState(key, options.action, options.binding) } : {}
|
|
34647
|
+
});
|
|
34648
|
+
};
|
|
34649
|
+
const response = inputResponse(ctx.mcpReq.inputResponses, requestKey);
|
|
34650
|
+
const accepted = acceptedContent(ctx.mcpReq.inputResponses, requestKey, confirmationSchema);
|
|
34651
|
+
if (response.kind === "missing") {
|
|
34652
|
+
if (callerAcceptsFormElicitation(ctx) === false) {
|
|
34653
|
+
return textResult({
|
|
34654
|
+
confirmed: false,
|
|
34655
|
+
dispatched: false,
|
|
34656
|
+
action: options.action,
|
|
34657
|
+
reason: "confirmation-unsupported",
|
|
34658
|
+
note: options.unsupportedNote ? `${UNSUPPORTED_NOTE}. ${options.unsupportedNote}` : `${UNSUPPORTED_NOTE}.`
|
|
34659
|
+
});
|
|
34660
|
+
}
|
|
34661
|
+
return ask();
|
|
34662
|
+
}
|
|
34663
|
+
if (response.kind === "elicit" && response.action === "accept" && accepted?.confirmed === true) {
|
|
34664
|
+
if (key && options.binding) {
|
|
34665
|
+
const state = echoedState(ctx);
|
|
34666
|
+
if (state === void 0 || state === null) {
|
|
34667
|
+
return errorResult(`Confirmation for ${options.action} was accepted, but the retry carried no requestState, so it cannot be checked against the prompt that was shown. Nothing was done. The likely cause is that the MCP client or host does not round-trip requestState (it must echo the requestState from the input_required result back on the retry); asking again would loop.`);
|
|
34668
|
+
}
|
|
34669
|
+
if (!verifyState(key, options.action, options.binding, state))
|
|
34670
|
+
return ask();
|
|
34671
|
+
}
|
|
34672
|
+
return void 0;
|
|
34673
|
+
}
|
|
34674
|
+
return textResult({
|
|
34675
|
+
confirmed: false,
|
|
34676
|
+
cancelled: true,
|
|
34677
|
+
action: options.action,
|
|
34678
|
+
note: "Nothing was changed because the confirmation was declined, cancelled, or left unchecked."
|
|
34679
|
+
});
|
|
34680
|
+
}
|
|
34681
|
+
|
|
34456
34682
|
// ../../node_modules/@chrischall/mcp-utils/dist/server/index.js
|
|
34457
34683
|
var SERVER_PROTOCOL_VERSIONS = Object.freeze([
|
|
34458
34684
|
"2026-07-28",
|
|
@@ -34547,13 +34773,47 @@ async function createMcpServer(opts) {
|
|
|
34547
34773
|
}
|
|
34548
34774
|
return server;
|
|
34549
34775
|
}
|
|
34776
|
+
var DEFAULT_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
34777
|
+
var DEFAULT_REPEAT_SIGNAL_GRACE_MS = 500;
|
|
34550
34778
|
function withGracefulShutdown(target, opts = {}) {
|
|
34551
34779
|
const shouldExit = opts.exit ?? true;
|
|
34780
|
+
if (opts.timeoutMs !== void 0 && !(Number.isFinite(opts.timeoutMs) && opts.timeoutMs > 0)) {
|
|
34781
|
+
throw new RangeError("withGracefulShutdown: timeoutMs must be a finite number greater than 0.");
|
|
34782
|
+
}
|
|
34783
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS;
|
|
34784
|
+
if (opts.repeatSignalGraceMs !== void 0 && !(Number.isFinite(opts.repeatSignalGraceMs) && opts.repeatSignalGraceMs >= 0)) {
|
|
34785
|
+
throw new RangeError("withGracefulShutdown: repeatSignalGraceMs must be a finite number >= 0.");
|
|
34786
|
+
}
|
|
34787
|
+
const repeatGraceMs = opts.repeatSignalGraceMs ?? DEFAULT_REPEAT_SIGNAL_GRACE_MS;
|
|
34552
34788
|
let shuttingDown = false;
|
|
34789
|
+
let firstSignalAt = 0;
|
|
34790
|
+
let exited = false;
|
|
34791
|
+
const exitOnce = () => {
|
|
34792
|
+
if (exited)
|
|
34793
|
+
return;
|
|
34794
|
+
exited = true;
|
|
34795
|
+
process.exit(0);
|
|
34796
|
+
};
|
|
34553
34797
|
const handler = (signal) => {
|
|
34554
|
-
if (shuttingDown)
|
|
34798
|
+
if (shuttingDown) {
|
|
34799
|
+
if (Date.now() - firstSignalAt < repeatGraceMs)
|
|
34800
|
+
return;
|
|
34801
|
+
if (shouldExit) {
|
|
34802
|
+
console.error(`[mcp-utils] second ${signal} during shutdown \u2014 exiting now`);
|
|
34803
|
+
exitOnce();
|
|
34804
|
+
}
|
|
34555
34805
|
return;
|
|
34806
|
+
}
|
|
34556
34807
|
shuttingDown = true;
|
|
34808
|
+
firstSignalAt = Date.now();
|
|
34809
|
+
let timer;
|
|
34810
|
+
if (shouldExit) {
|
|
34811
|
+
timer = setTimeout(() => {
|
|
34812
|
+
console.error(`[mcp-utils] graceful shutdown on ${signal} did not finish in ${timeoutMs}ms \u2014 exiting`);
|
|
34813
|
+
exitOnce();
|
|
34814
|
+
}, timeoutMs);
|
|
34815
|
+
timer.unref?.();
|
|
34816
|
+
}
|
|
34557
34817
|
void (async () => {
|
|
34558
34818
|
try {
|
|
34559
34819
|
if (opts.onSignal)
|
|
@@ -34562,8 +34822,10 @@ function withGracefulShutdown(target, opts = {}) {
|
|
|
34562
34822
|
} catch (err) {
|
|
34563
34823
|
console.error(`[mcp-utils] error during graceful shutdown on ${signal}: ${err instanceof Error ? err.message : String(err)}`);
|
|
34564
34824
|
} finally {
|
|
34825
|
+
if (timer !== void 0)
|
|
34826
|
+
clearTimeout(timer);
|
|
34565
34827
|
if (shouldExit)
|
|
34566
|
-
|
|
34828
|
+
exitOnce();
|
|
34567
34829
|
}
|
|
34568
34830
|
})();
|
|
34569
34831
|
};
|
|
@@ -34593,6 +34855,8 @@ function runMcp(opts) {
|
|
|
34593
34855
|
}
|
|
34594
34856
|
|
|
34595
34857
|
// ../../node_modules/@chrischall/mcp-utils/dist/config/index.js
|
|
34858
|
+
import { homedir } from "node:os";
|
|
34859
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
34596
34860
|
var PLACEHOLDER_RE = /^\$\{[^}]*\}$/;
|
|
34597
34861
|
function readEnvVar(key, opts = {}) {
|
|
34598
34862
|
const env = opts.env ?? process.env;
|
|
@@ -34619,6 +34883,50 @@ function parseBoolEnv(key, opts = {}) {
|
|
|
34619
34883
|
return false;
|
|
34620
34884
|
return fallback;
|
|
34621
34885
|
}
|
|
34886
|
+
function expandPath(p) {
|
|
34887
|
+
let expanded = p;
|
|
34888
|
+
if (p === "~") {
|
|
34889
|
+
expanded = homedir();
|
|
34890
|
+
} else if (p.startsWith("~/")) {
|
|
34891
|
+
expanded = join(homedir(), p.slice(2));
|
|
34892
|
+
}
|
|
34893
|
+
return isAbsolute(expanded) ? expanded : resolve(expanded);
|
|
34894
|
+
}
|
|
34895
|
+
|
|
34896
|
+
// ../../node_modules/@chrischall/mcp-utils/dist/fs/confine.js
|
|
34897
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
34898
|
+
import { dirname, isAbsolute as isAbsolute2, relative, resolve as resolve2, sep } from "node:path";
|
|
34899
|
+
function isInside(root, target) {
|
|
34900
|
+
const rel = relative(root, target);
|
|
34901
|
+
return rel === "" || !rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute2(rel);
|
|
34902
|
+
}
|
|
34903
|
+
function realPathAllowingMissing(p) {
|
|
34904
|
+
const abs = resolve2(p);
|
|
34905
|
+
let existing = abs;
|
|
34906
|
+
const tail = [];
|
|
34907
|
+
while (!existsSync(existing)) {
|
|
34908
|
+
const parent = dirname(existing);
|
|
34909
|
+
if (parent === existing)
|
|
34910
|
+
break;
|
|
34911
|
+
tail.unshift(existing.slice(parent.length).replace(/^[/\\]+/, ""));
|
|
34912
|
+
existing = parent;
|
|
34913
|
+
}
|
|
34914
|
+
let real;
|
|
34915
|
+
try {
|
|
34916
|
+
real = realpathSync(existing);
|
|
34917
|
+
} catch {
|
|
34918
|
+
real = existing;
|
|
34919
|
+
}
|
|
34920
|
+
return tail.length > 0 ? resolve2(real, ...tail) : real;
|
|
34921
|
+
}
|
|
34922
|
+
function assertPathWithinRoots(path, roots) {
|
|
34923
|
+
const target = realPathAllowingMissing(expandPath(path));
|
|
34924
|
+
for (const root of roots) {
|
|
34925
|
+
if (isInside(realPathAllowingMissing(expandPath(root)), target))
|
|
34926
|
+
return target;
|
|
34927
|
+
}
|
|
34928
|
+
throw new Error(`Path is outside the allowed directories: ${path}`);
|
|
34929
|
+
}
|
|
34622
34930
|
|
|
34623
34931
|
// ../../node_modules/@chrischall/mcp-utils/dist/zod/index.js
|
|
34624
34932
|
var PositiveInt = external_exports.number().int().positive();
|
|
@@ -34640,7 +34948,7 @@ var pageSchema = {
|
|
|
34640
34948
|
};
|
|
34641
34949
|
|
|
34642
34950
|
// ../gogcli-mcp/src/runner.ts
|
|
34643
|
-
import { delimiter, join } from "node:path";
|
|
34951
|
+
import { delimiter, join as join2 } from "node:path";
|
|
34644
34952
|
|
|
34645
34953
|
// ../gogcli-mcp/src/timestamps.ts
|
|
34646
34954
|
var DEFAULT_DISPLAY_TZ = "America/New_York";
|
|
@@ -34666,9 +34974,9 @@ function offsetAt(instant, tz) {
|
|
|
34666
34974
|
const w = wallPartsIn(instant, tz);
|
|
34667
34975
|
const asUTC = Date.UTC(w.year, w.month - 1, w.day, w.hour, w.minute, w.second, instant.getUTCMilliseconds());
|
|
34668
34976
|
const minutes = Math.round((asUTC - instant.getTime()) / 6e4);
|
|
34669
|
-
const
|
|
34977
|
+
const sign2 = minutes < 0 ? "-" : "+";
|
|
34670
34978
|
const abs = Math.abs(minutes);
|
|
34671
|
-
return `${
|
|
34979
|
+
return `${sign2}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`;
|
|
34672
34980
|
}
|
|
34673
34981
|
function wallPartsIn(instant, tz) {
|
|
34674
34982
|
const parts = new Intl.DateTimeFormat("en-US", {
|
|
@@ -34841,9 +35149,48 @@ function normalizeTimestamps(text, tz = displayTimeZone(), naiveTz = naiveSource
|
|
|
34841
35149
|
return JSON.stringify(parsed, null, detectIndent(text));
|
|
34842
35150
|
}
|
|
34843
35151
|
|
|
35152
|
+
// ../gogcli-mcp/src/arg-guard.ts
|
|
35153
|
+
var FORBIDDEN_LONG_FLAG = /^--(readonly|enable-commands|enable-commands-exact|disable-commands|access-token|home|account|acct|client|gmail-no-send|no-input|non-interactive|noninteractive)(=|$)/i;
|
|
35154
|
+
var SHORT_ACCOUNT_CLUSTER = /^-(?!-)[A-Za-z]*a/;
|
|
35155
|
+
function forbiddenArgReason(arg) {
|
|
35156
|
+
if (arg === "--") {
|
|
35157
|
+
return 'The argument "--" is not allowed: it ends flag parsing and would disable safety flags appended after it.';
|
|
35158
|
+
}
|
|
35159
|
+
const match = FORBIDDEN_LONG_FLAG.exec(arg);
|
|
35160
|
+
if (match) {
|
|
35161
|
+
return `The flag ${arg} is not allowed here: --${match[1].toLowerCase()} is a safety/credential control set by the server operator, not a per-call option.`;
|
|
35162
|
+
}
|
|
35163
|
+
if (SHORT_ACCOUNT_CLUSTER.test(arg)) {
|
|
35164
|
+
return `The flag ${arg} is not allowed here: -a selects the account. Use the tool's account parameter instead.`;
|
|
35165
|
+
}
|
|
35166
|
+
return void 0;
|
|
35167
|
+
}
|
|
35168
|
+
function assertSafeForwardedArgs(args) {
|
|
35169
|
+
for (const arg of args) {
|
|
35170
|
+
const reason = forbiddenArgReason(arg);
|
|
35171
|
+
if (reason) throw new Error(reason);
|
|
35172
|
+
}
|
|
35173
|
+
}
|
|
35174
|
+
var SUBCOMMAND_SHAPE = /^[a-z][a-z0-9-]*$/;
|
|
35175
|
+
function assertSafeSubcommand(subcommand) {
|
|
35176
|
+
if (!SUBCOMMAND_SHAPE.test(subcommand)) {
|
|
35177
|
+
throw new Error(
|
|
35178
|
+
`Invalid subcommand ${JSON.stringify(subcommand)}: pass a single gog subcommand name such as "list" or "mark-read", and put its arguments in args.`
|
|
35179
|
+
);
|
|
35180
|
+
}
|
|
35181
|
+
}
|
|
35182
|
+
|
|
35183
|
+
// ../gogcli-mcp/src/argv.ts
|
|
35184
|
+
function pos(value) {
|
|
35185
|
+
return { kind: "positional", value };
|
|
35186
|
+
}
|
|
35187
|
+
function isGogPositional(arg) {
|
|
35188
|
+
return typeof arg === "object" && arg !== null && arg.kind === "positional";
|
|
35189
|
+
}
|
|
35190
|
+
|
|
34844
35191
|
// ../gogcli-mcp/src/runner.ts
|
|
34845
35192
|
function isGogFileArg(arg) {
|
|
34846
|
-
return typeof arg !== "string";
|
|
35193
|
+
return typeof arg !== "string" && arg.kind === "file";
|
|
34847
35194
|
}
|
|
34848
35195
|
var TIMEOUT_MS = 3e4;
|
|
34849
35196
|
function readonlyEnvEnabled() {
|
|
@@ -34927,8 +35274,8 @@ function formatTimeout(ms) {
|
|
|
34927
35274
|
}
|
|
34928
35275
|
async function spawnWithTempFiles(args, opts) {
|
|
34929
35276
|
const { mkdtemp, mkdir, writeFile, rm } = await import("node:fs/promises");
|
|
34930
|
-
const { tmpdir } = await import("node:os");
|
|
34931
|
-
const dir = await mkdtemp(
|
|
35277
|
+
const { tmpdir: tmpdir2 } = await import("node:os");
|
|
35278
|
+
const dir = await mkdtemp(join2(tmpdir2(), "gogcli-mcp-"));
|
|
34932
35279
|
try {
|
|
34933
35280
|
const argv = [];
|
|
34934
35281
|
let seq = 0;
|
|
@@ -34937,10 +35284,10 @@ async function spawnWithTempFiles(args, opts) {
|
|
|
34937
35284
|
argv.push(arg);
|
|
34938
35285
|
continue;
|
|
34939
35286
|
}
|
|
34940
|
-
const sub =
|
|
35287
|
+
const sub = join2(dir, String(seq));
|
|
34941
35288
|
seq += 1;
|
|
34942
35289
|
await mkdir(sub, { recursive: true, mode: 448 });
|
|
34943
|
-
const path =
|
|
35290
|
+
const path = join2(sub, arg.filename ?? `${arg.flag}.${arg.ext ?? "txt"}`);
|
|
34944
35291
|
const data = arg.encoding === "base64" ? Buffer.from(arg.contents, "base64") : Buffer.from(arg.contents, "utf8");
|
|
34945
35292
|
await writeFile(path, data, { mode: 384 });
|
|
34946
35293
|
argv.push(arg.positional ? path : `--${arg.flag}=${path}`);
|
|
@@ -34958,10 +35305,10 @@ function spawnExecutor(args, opts) {
|
|
|
34958
35305
|
return spawnGog(args, opts);
|
|
34959
35306
|
}
|
|
34960
35307
|
async function spawnGog(fullArgs, opts) {
|
|
34961
|
-
const { timeout, interactive = false, spawner, binary = false } = opts;
|
|
35308
|
+
const { timeout, interactive = false, spawner, binary = false, maxOutputBytes } = opts;
|
|
34962
35309
|
const spawn = spawner ?? (await import("node:child_process")).spawn;
|
|
34963
35310
|
const effectiveTimeout = timeout ?? TIMEOUT_MS;
|
|
34964
|
-
return new Promise((
|
|
35311
|
+
return new Promise((resolve4, reject) => {
|
|
34965
35312
|
const childEnv = { ...sanitizedEnv(), GOG_TIMEZONE: naiveSourceTimeZone(), PATH: augmentedPath() };
|
|
34966
35313
|
const child = spawn(readEnvVar("GOG_PATH") ?? "gog", fullArgs, { env: childEnv });
|
|
34967
35314
|
const stdoutChunks = [];
|
|
@@ -34979,7 +35326,17 @@ async function spawnGog(fullArgs, opts) {
|
|
|
34979
35326
|
child.kill();
|
|
34980
35327
|
reject(new Error(`gog timed out after ${formatTimeout(effectiveTimeout)}`));
|
|
34981
35328
|
}, effectiveTimeout);
|
|
35329
|
+
let stdoutBytes = 0;
|
|
34982
35330
|
child.stdout.on("data", (chunk) => {
|
|
35331
|
+
if (settled) return;
|
|
35332
|
+
stdoutBytes += chunk.length;
|
|
35333
|
+
if (maxOutputBytes !== void 0 && stdoutBytes > maxOutputBytes) {
|
|
35334
|
+
settled = true;
|
|
35335
|
+
stopWatching();
|
|
35336
|
+
child.kill();
|
|
35337
|
+
reject(new Error(`gog output exceeded the limit: more than ${maxOutputBytes} bytes`));
|
|
35338
|
+
return;
|
|
35339
|
+
}
|
|
34983
35340
|
stdoutChunks.push(chunk);
|
|
34984
35341
|
});
|
|
34985
35342
|
child.stderr.on("data", (chunk) => {
|
|
@@ -34996,14 +35353,14 @@ async function spawnGog(fullArgs, opts) {
|
|
|
34996
35353
|
const stderr = Buffer.concat(stderrChunks).toString().trim();
|
|
34997
35354
|
if (code === 0) {
|
|
34998
35355
|
if (binary) {
|
|
34999
|
-
|
|
35356
|
+
resolve4(Buffer.concat(stdoutChunks).toString("base64"));
|
|
35000
35357
|
return;
|
|
35001
35358
|
}
|
|
35002
35359
|
const stdout = Buffer.concat(stdoutChunks).toString();
|
|
35003
35360
|
if (interactive && stderr) {
|
|
35004
|
-
|
|
35361
|
+
resolve4(stdout + "\n" + stderr);
|
|
35005
35362
|
} else {
|
|
35006
|
-
|
|
35363
|
+
resolve4(stdout);
|
|
35007
35364
|
}
|
|
35008
35365
|
} else {
|
|
35009
35366
|
reject(new Error(stderr || `gog exited with code ${code}`));
|
|
@@ -35023,7 +35380,33 @@ async function spawnGog(fullArgs, opts) {
|
|
|
35023
35380
|
});
|
|
35024
35381
|
});
|
|
35025
35382
|
}
|
|
35383
|
+
function placePositionals(args) {
|
|
35384
|
+
const flags = [];
|
|
35385
|
+
const positionals = [];
|
|
35386
|
+
let afterSeparator = false;
|
|
35387
|
+
for (const arg of args) {
|
|
35388
|
+
if (arg === "--") {
|
|
35389
|
+
afterSeparator = true;
|
|
35390
|
+
} else if (isGogPositional(arg)) {
|
|
35391
|
+
positionals.push(arg.value);
|
|
35392
|
+
} else if (afterSeparator || isGogFileArg(arg) && arg.positional) {
|
|
35393
|
+
positionals.push(arg);
|
|
35394
|
+
} else {
|
|
35395
|
+
flags.push(arg);
|
|
35396
|
+
}
|
|
35397
|
+
}
|
|
35398
|
+
return { flags, positionals };
|
|
35399
|
+
}
|
|
35400
|
+
function assertNoSafetyOverrides(flags) {
|
|
35401
|
+
for (const arg of flags) {
|
|
35402
|
+
if (typeof arg !== "string") continue;
|
|
35403
|
+
const reason = forbiddenArgReason(arg);
|
|
35404
|
+
if (reason) throw new Error(reason);
|
|
35405
|
+
}
|
|
35406
|
+
}
|
|
35026
35407
|
function assembleArgs(args, opts) {
|
|
35408
|
+
const { flags, positionals } = placePositionals(args);
|
|
35409
|
+
assertNoSafetyOverrides(flags);
|
|
35027
35410
|
const effectiveAccount = opts.account ?? readEnvVar("GOG_ACCOUNT");
|
|
35028
35411
|
const fullArgs = ["--json", "--color=never"];
|
|
35029
35412
|
if (!opts.interactive) {
|
|
@@ -35032,17 +35415,21 @@ function assembleArgs(args, opts) {
|
|
|
35032
35415
|
if (opts.readonly || readonlyEnvEnabled()) {
|
|
35033
35416
|
fullArgs.push("--readonly");
|
|
35034
35417
|
}
|
|
35418
|
+
if (opts.gmailNoSend) {
|
|
35419
|
+
fullArgs.push("--gmail-no-send");
|
|
35420
|
+
}
|
|
35035
35421
|
if (effectiveAccount) {
|
|
35036
35422
|
fullArgs.push("--account", effectiveAccount);
|
|
35037
35423
|
}
|
|
35038
|
-
fullArgs.push(...
|
|
35424
|
+
fullArgs.push(...flags);
|
|
35425
|
+
if (positionals.length > 0) fullArgs.push("--", ...positionals);
|
|
35039
35426
|
return fullArgs;
|
|
35040
35427
|
}
|
|
35041
35428
|
async function run(args, options = {}) {
|
|
35042
|
-
const { account, spawner, interactive = false, timeout, readonly: readonly2 = false, redactMode = "full", opaqueFields } = options;
|
|
35429
|
+
const { account, spawner, interactive = false, timeout, readonly: readonly2 = false, redactMode = "full", opaqueFields, gmailNoSend = false } = options;
|
|
35043
35430
|
const base = redactMode === "tokens" ? redactGoogleTokens : redactSecrets2;
|
|
35044
35431
|
const redact = opaqueFields?.length ? (text) => redactPreservingOpaqueFields(text, opaqueFields, base) : base;
|
|
35045
|
-
const fullArgs = assembleArgs(args, { account, interactive, readonly: readonly2 });
|
|
35432
|
+
const fullArgs = assembleArgs(args, { account, interactive, readonly: readonly2, gmailNoSend });
|
|
35046
35433
|
try {
|
|
35047
35434
|
return redact(await spawnExecutor(fullArgs, { timeout, interactive, spawner }));
|
|
35048
35435
|
} catch (err) {
|
|
@@ -35070,6 +35457,107 @@ function stripConsumedPageToken(text) {
|
|
|
35070
35457
|
return JSON.stringify(obj, null, detectIndent2(text));
|
|
35071
35458
|
}
|
|
35072
35459
|
|
|
35460
|
+
// ../gogcli-mcp/src/file-roots.ts
|
|
35461
|
+
import { homedir as homedir2 } from "node:os";
|
|
35462
|
+
import { delimiter as delimiter2, join as join4 } from "node:path";
|
|
35463
|
+
|
|
35464
|
+
// ../gogcli-mcp/src/attachment-root.ts
|
|
35465
|
+
import { tmpdir, userInfo } from "node:os";
|
|
35466
|
+
import { dirname as dirname2, join as join3, relative as relative2, resolve as resolve3, isAbsolute as isAbsolute3, sep as sep2 } from "node:path";
|
|
35467
|
+
var ATTACHMENT_DOWNLOAD_ROOT = join3(tmpdir(), `gogcli-mcp-attachments-${userInfo().uid}`);
|
|
35468
|
+
var ATTACHMENT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
35469
|
+
|
|
35470
|
+
// ../gogcli-mcp/src/file-roots.ts
|
|
35471
|
+
var FILE_ROOTS_ENV = "GOG_FILE_ROOTS";
|
|
35472
|
+
function defaultFileRoot() {
|
|
35473
|
+
return join4(homedir2(), "gogcli-mcp-files");
|
|
35474
|
+
}
|
|
35475
|
+
function fileRoots() {
|
|
35476
|
+
const raw = readEnvVar(FILE_ROOTS_ENV);
|
|
35477
|
+
const roots = (raw ?? "").split(delimiter2).map((r) => r.trim()).filter(Boolean);
|
|
35478
|
+
return roots.length > 0 ? roots : [defaultFileRoot()];
|
|
35479
|
+
}
|
|
35480
|
+
function confinePath(path, param, opts = {}) {
|
|
35481
|
+
if (opts.allowDash && path === "-") return path;
|
|
35482
|
+
const roots = [...fileRoots(), ATTACHMENT_DOWNLOAD_ROOT];
|
|
35483
|
+
try {
|
|
35484
|
+
assertPathWithinRoots(path, roots);
|
|
35485
|
+
} catch {
|
|
35486
|
+
throw new Error(
|
|
35487
|
+
`${param} ${JSON.stringify(path)} is outside the directories this server may read or write (${roots.join(", ")}). Put the file in one of them, or ask the server operator to widen ${FILE_ROOTS_ENV}.`
|
|
35488
|
+
);
|
|
35489
|
+
}
|
|
35490
|
+
return path;
|
|
35491
|
+
}
|
|
35492
|
+
|
|
35493
|
+
// ../gogcli-mcp/src/run-path-guard.ts
|
|
35494
|
+
var POSITIONAL_PATH_SUBCOMMANDS = {
|
|
35495
|
+
appscript: { pull: "gog_appscript_pull" },
|
|
35496
|
+
drive: { upload: "gog_drive_upload", sync: "gog_drive_sync_push" },
|
|
35497
|
+
gmail: { import: "gog_gmail_import" },
|
|
35498
|
+
slides: {
|
|
35499
|
+
"add-slide": "gog_slides_add_slide",
|
|
35500
|
+
"insert-image": "gog_slides_insert_image",
|
|
35501
|
+
"replace-slide": "gog_slides_replace_slide"
|
|
35502
|
+
}
|
|
35503
|
+
};
|
|
35504
|
+
var PATH_FLAGS = /* @__PURE__ */ new Set(["out", "output", "out-dir", "output-dir", "dir", "worker-dir", "attach", "file", "key", "cert", "replacements"]);
|
|
35505
|
+
var ID_FLAGS = {
|
|
35506
|
+
drive: /* @__PURE__ */ new Set(["file", "filter-file"])
|
|
35507
|
+
};
|
|
35508
|
+
var EXEC_FLAGS = /* @__PURE__ */ new Set(["on-change", "on-new", "mmdc"]);
|
|
35509
|
+
var SHORT_PATH_CLUSTER = /^-(?!-)[A-Za-z]*[of]/;
|
|
35510
|
+
var LONG_FLAG = /^--([A-Za-z0-9][A-Za-z0-9-]*)(?:=([\s\S]*))?$/;
|
|
35511
|
+
function isPathFlag(service, name) {
|
|
35512
|
+
if (ID_FLAGS[service]?.has(name)) return false;
|
|
35513
|
+
return PATH_FLAGS.has(name) || name.endsWith("-file");
|
|
35514
|
+
}
|
|
35515
|
+
function confineFlagValue(flag, value) {
|
|
35516
|
+
if (value === "-") return;
|
|
35517
|
+
confinePath(value, flag);
|
|
35518
|
+
for (const part of value.split(",")) {
|
|
35519
|
+
if (part !== "" && part !== "-") confinePath(part, flag);
|
|
35520
|
+
}
|
|
35521
|
+
}
|
|
35522
|
+
function positionalPathTool(service, subcommand) {
|
|
35523
|
+
return POSITIONAL_PATH_SUBCOMMANDS[service]?.[subcommand];
|
|
35524
|
+
}
|
|
35525
|
+
function assertRunPathsConfined(service, subcommand, args) {
|
|
35526
|
+
const dedicated = positionalPathTool(service, subcommand);
|
|
35527
|
+
if (dedicated) {
|
|
35528
|
+
throw new Error(
|
|
35529
|
+
`gog ${service} ${subcommand} reads or writes a local path given as a positional argument, so it is not available through gog_${service}_run. Use ${dedicated}, which confines the path to GOG_FILE_ROOTS.`
|
|
35530
|
+
);
|
|
35531
|
+
}
|
|
35532
|
+
for (let i = 0; i < args.length; i++) {
|
|
35533
|
+
const arg = args[i];
|
|
35534
|
+
if (SHORT_PATH_CLUSTER.test(arg)) {
|
|
35535
|
+
throw new Error(
|
|
35536
|
+
`The flag ${arg} is not allowed here: spell a path flag out as --out or --file (e.g. --out=<path>) so its path can be checked against GOG_FILE_ROOTS.`
|
|
35537
|
+
);
|
|
35538
|
+
}
|
|
35539
|
+
const match = LONG_FLAG.exec(arg);
|
|
35540
|
+
if (!match) continue;
|
|
35541
|
+
const name = match[1].toLowerCase();
|
|
35542
|
+
if (EXEC_FLAGS.has(name)) {
|
|
35543
|
+
throw new Error(`The flag --${name} is not allowed here: it runs a local program chosen by the caller.`);
|
|
35544
|
+
}
|
|
35545
|
+
const isJson = name.endsWith("-json");
|
|
35546
|
+
if (!isJson && !isPathFlag(service, name)) continue;
|
|
35547
|
+
let value = match[2];
|
|
35548
|
+
if (value === void 0) {
|
|
35549
|
+
value = args[i + 1];
|
|
35550
|
+
if (value === void 0) continue;
|
|
35551
|
+
i++;
|
|
35552
|
+
}
|
|
35553
|
+
if (isJson) {
|
|
35554
|
+
if (value.startsWith("@")) confineFlagValue(`--${name}`, value.slice(1));
|
|
35555
|
+
continue;
|
|
35556
|
+
}
|
|
35557
|
+
confineFlagValue(`--${name}`, value);
|
|
35558
|
+
}
|
|
35559
|
+
}
|
|
35560
|
+
|
|
35073
35561
|
// ../gogcli-mcp/src/tools/utils.ts
|
|
35074
35562
|
var accountParam = external_exports.string().optional().describe(
|
|
35075
35563
|
"Google account email to use, e.g. you@gmail.com \u2014 must be the full address, not a bare username. Overrides the GOG_ACCOUNT env var. Omit to use the single configured account."
|
|
@@ -35114,9 +35602,17 @@ var paginationParams = {
|
|
|
35114
35602
|
all: external_exports.boolean().optional().describe("Fetch all pages")
|
|
35115
35603
|
};
|
|
35116
35604
|
function registerRunTool(server, options) {
|
|
35117
|
-
const { service, examples, omitAccount = false, note } = options;
|
|
35605
|
+
const { service, examples, omitAccount = false, note, allowedSubcommands, vet, gmailNoSend = false } = options;
|
|
35606
|
+
for (const [, example] of examples.matchAll(/"([^"]+)"/g)) {
|
|
35607
|
+
const refused = allowedSubcommands && !allowedSubcommands.includes(example) || vet?.(example, []) || positionalPathTool(service, example);
|
|
35608
|
+
if (refused) {
|
|
35609
|
+
throw new Error(`gog_${service}_run example "${example}" is refused by the tool itself; list a subcommand it runs.`);
|
|
35610
|
+
}
|
|
35611
|
+
}
|
|
35118
35612
|
const baseDescription = `Run any gog ${service} subcommand not covered by the other tools. Run \`gog ${service} --help\` for the full list of subcommands, or \`gog ${service} <subcommand> --help\` for flags on a specific subcommand.`;
|
|
35119
|
-
const
|
|
35613
|
+
const restriction = allowedSubcommands ? ` Only these subcommands are available: ${allowedSubcommands.join(", ")}.` : "";
|
|
35614
|
+
const safety = ' Flags that override server safety controls (--readonly, --enable-commands, --disable-commands, --account, --access-token, --home, --client, --gmail-no-send, --no-input) and a bare "--" are refused. Local file paths (--out, --out-dir, --attach, --file, --*-file, @file JSON) must be inside GOG_FILE_ROOTS; subcommands that take a local path positionally (e.g. drive upload) are refused in favour of their dedicated tools.';
|
|
35615
|
+
const description = `${baseDescription}${restriction}${safety}${note ? ` ${note}` : ""}`;
|
|
35120
35616
|
const inputSchema = {
|
|
35121
35617
|
subcommand: external_exports.string().describe(`The gog ${service} subcommand to run, e.g. ${examples}`),
|
|
35122
35618
|
args: external_exports.array(external_exports.string()).describe("Additional positional args and flags")
|
|
@@ -35130,7 +35626,21 @@ function registerRunTool(server, options) {
|
|
|
35130
35626
|
inputSchema: external_exports.object(inputSchema)
|
|
35131
35627
|
}, async (rawArgs) => {
|
|
35132
35628
|
const { subcommand, args, account } = rawArgs;
|
|
35133
|
-
|
|
35629
|
+
try {
|
|
35630
|
+
assertSafeSubcommand(subcommand);
|
|
35631
|
+
assertSafeForwardedArgs(args);
|
|
35632
|
+
if (allowedSubcommands && !allowedSubcommands.includes(subcommand)) {
|
|
35633
|
+
throw new Error(
|
|
35634
|
+
`gog ${service} ${subcommand} is not available through gog_${service}_run. Allowed: ${allowedSubcommands.join(", ")}.`
|
|
35635
|
+
);
|
|
35636
|
+
}
|
|
35637
|
+
const refusal = vet?.(subcommand, args);
|
|
35638
|
+
if (refusal) throw new Error(refusal);
|
|
35639
|
+
assertRunPathsConfined(service, subcommand, args);
|
|
35640
|
+
} catch (err) {
|
|
35641
|
+
return errorResult(errorText(err));
|
|
35642
|
+
}
|
|
35643
|
+
return runOrDiagnose([service, subcommand, ...args], gmailNoSend ? { account, gmailNoSend } : { account });
|
|
35134
35644
|
});
|
|
35135
35645
|
}
|
|
35136
35646
|
function errorText(err) {
|
|
@@ -35251,7 +35761,185 @@ function formatAuthHealth(raw, now) {
|
|
|
35251
35761
|
return accounts.map((a) => formatOneAccountHealth(a, now)).join("\n\n");
|
|
35252
35762
|
}
|
|
35253
35763
|
|
|
35764
|
+
// ../gogcli-mcp/src/send-confirm-token.ts
|
|
35765
|
+
import { createHash as createHash2, createHmac as createHmac2, randomBytes, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
35766
|
+
var CONFIRM_TOKEN_TTL_DEFAULT_SECONDS = 600;
|
|
35767
|
+
var PREFIX = "gct1";
|
|
35768
|
+
var secret;
|
|
35769
|
+
var spent = /* @__PURE__ */ new Map();
|
|
35770
|
+
function sendConfirmFallbackEnabled() {
|
|
35771
|
+
return readEnvVar("GOG_SEND_CONFIRM_FALLBACK")?.trim().toLowerCase() === "token";
|
|
35772
|
+
}
|
|
35773
|
+
function confirmTokenTtlSeconds() {
|
|
35774
|
+
const raw = readEnvVar("GOG_CONFIRM_TTL_SECONDS")?.trim();
|
|
35775
|
+
if (raw && /^\d+$/.test(raw)) {
|
|
35776
|
+
const n = Number(raw);
|
|
35777
|
+
if (n > 0) return n;
|
|
35778
|
+
}
|
|
35779
|
+
return CONFIRM_TOKEN_TTL_DEFAULT_SECONDS;
|
|
35780
|
+
}
|
|
35781
|
+
function hmacKey() {
|
|
35782
|
+
if (!secret) {
|
|
35783
|
+
const configured = readEnvVar("GOG_CONFIRM_SECRET");
|
|
35784
|
+
secret = configured ? Buffer.from(configured, "utf8") : randomBytes(32);
|
|
35785
|
+
}
|
|
35786
|
+
return secret;
|
|
35787
|
+
}
|
|
35788
|
+
function canonicalize(value) {
|
|
35789
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
35790
|
+
if (value && typeof value === "object") {
|
|
35791
|
+
const out = {};
|
|
35792
|
+
for (const key of Object.keys(value).sort()) {
|
|
35793
|
+
const v = value[key];
|
|
35794
|
+
if (v !== void 0) out[key] = canonicalize(v);
|
|
35795
|
+
}
|
|
35796
|
+
return out;
|
|
35797
|
+
}
|
|
35798
|
+
return value;
|
|
35799
|
+
}
|
|
35800
|
+
function hashSendPayload(payload) {
|
|
35801
|
+
return createHash2("sha256").update(JSON.stringify(canonicalize(payload))).digest("hex");
|
|
35802
|
+
}
|
|
35803
|
+
function sign(body) {
|
|
35804
|
+
return createHmac2("sha256", hmacKey()).update(`${PREFIX}.${body}`).digest("base64url");
|
|
35805
|
+
}
|
|
35806
|
+
function issueConfirmToken(binding, now = Date.now()) {
|
|
35807
|
+
const exp = now + confirmTokenTtlSeconds() * 1e3;
|
|
35808
|
+
const claims = {
|
|
35809
|
+
t: binding.tool,
|
|
35810
|
+
a: binding.account,
|
|
35811
|
+
g: binding.target,
|
|
35812
|
+
...binding.revision === void 0 ? {} : { r: binding.revision },
|
|
35813
|
+
h: binding.payloadHash,
|
|
35814
|
+
iat: now,
|
|
35815
|
+
exp,
|
|
35816
|
+
n: randomBytes(16).toString("base64url")
|
|
35817
|
+
};
|
|
35818
|
+
const body = Buffer.from(JSON.stringify(claims), "utf8").toString("base64url");
|
|
35819
|
+
return { token: `${PREFIX}.${body}.${sign(body)}`, expiresAt: new Date(exp).toISOString() };
|
|
35820
|
+
}
|
|
35821
|
+
function signatureMatches(body, sig) {
|
|
35822
|
+
const expected = Buffer.from(sign(body), "utf8");
|
|
35823
|
+
const actual = Buffer.from(sig, "utf8");
|
|
35824
|
+
return expected.length === actual.length && timingSafeEqual2(expected, actual);
|
|
35825
|
+
}
|
|
35826
|
+
function parseClaims(body) {
|
|
35827
|
+
try {
|
|
35828
|
+
return JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
|
|
35829
|
+
} catch {
|
|
35830
|
+
return void 0;
|
|
35831
|
+
}
|
|
35832
|
+
}
|
|
35833
|
+
function verifyConfirmToken(token, binding, now = Date.now()) {
|
|
35834
|
+
for (const [nonce, exp] of spent) if (exp < now) spent.delete(nonce);
|
|
35835
|
+
const parts = token.split(".");
|
|
35836
|
+
if (parts.length !== 3 || parts[0] !== PREFIX || !parts[1] || !parts[2]) return { ok: false, error: "TOKEN_INVALID" };
|
|
35837
|
+
const [, body, sig] = parts;
|
|
35838
|
+
if (!signatureMatches(body, sig)) return { ok: false, error: "TOKEN_INVALID" };
|
|
35839
|
+
const claims = parseClaims(body);
|
|
35840
|
+
if (!claims) return { ok: false, error: "TOKEN_INVALID" };
|
|
35841
|
+
if (claims.t !== binding.tool || claims.a !== binding.account || claims.g !== binding.target) {
|
|
35842
|
+
return { ok: false, error: "TOKEN_INVALID" };
|
|
35843
|
+
}
|
|
35844
|
+
if (spent.has(claims.n)) return { ok: false, error: "TOKEN_REUSED" };
|
|
35845
|
+
if (now > claims.exp) return { ok: false, error: "TOKEN_EXPIRED" };
|
|
35846
|
+
if (claims.r !== binding.revision) return { ok: false, error: "DRAFT_CHANGED", reason: "message-id-rotated" };
|
|
35847
|
+
if (claims.h !== binding.payloadHash) return { ok: false, error: "DRAFT_CHANGED", reason: "payload-changed" };
|
|
35848
|
+
spent.set(claims.n, claims.exp);
|
|
35849
|
+
return { ok: true };
|
|
35850
|
+
}
|
|
35851
|
+
|
|
35852
|
+
// ../gogcli-mcp/src/dispatch-confirmation.ts
|
|
35853
|
+
var BODY_PREVIEW_MAX = 2048;
|
|
35854
|
+
function bodyPreview(text) {
|
|
35855
|
+
if (!text) return void 0;
|
|
35856
|
+
if (text.length <= BODY_PREVIEW_MAX) return text;
|
|
35857
|
+
return `${text.slice(0, BODY_PREVIEW_MAX)}\u2026 [${text.length - BODY_PREVIEW_MAX} more characters not shown]`;
|
|
35858
|
+
}
|
|
35859
|
+
var CONFIRM_ACTION_INSTRUCTION = "Show this preview to the user verbatim and proceed only after they explicitly approve in chat. Then call again with confirmToken.";
|
|
35860
|
+
var FALLBACK_HINT = "Or set GOG_SEND_CONFIRM_FALLBACK=token to enable two-step confirmation.";
|
|
35861
|
+
var CONFIRM_FALLBACK_DESCRIPTION = ' If the client cannot show that prompt (no MCP elicitation, e.g. claude.ai) and the server sets GOG_SEND_CONFIRM_FALLBACK=token, a two-step flow applies instead: call WITHOUT confirmToken and nothing is sent or changed \u2014 the result has status "confirmation-required", the full preview and a confirmToken. Show that preview to the user verbatim; only after they explicitly approve it in chat, call again with the SAME arguments plus confirmToken. The tool re-reads what it would act on and refuses (DRAFT_CHANGED, with a fresh preview and token) if it changed; TOKEN_EXPIRED / TOKEN_REUSED / TOKEN_INVALID also do nothing.';
|
|
35862
|
+
var confirmTokenParam = external_exports.string().optional().describe(
|
|
35863
|
+
`ONLY for the two-step fallback (client without MCP elicitation, server with GOG_SEND_CONFIRM_FALLBACK=token). The confirmToken from this same tool's phase-1 "confirmation-required" response, passed back ONLY after the user has seen that preview and explicitly approved it in chat \u2014 never on the first call, never invented, never reused. Call again with the same arguments. Ignored when the client supports elicitation.`
|
|
35864
|
+
);
|
|
35865
|
+
var TOKEN_ERROR_NOTE = {
|
|
35866
|
+
TOKEN_EXPIRED: "Nothing was sent or changed: the confirmToken expired. Call again WITHOUT confirmToken for a fresh preview, and ask the user to approve it again.",
|
|
35867
|
+
TOKEN_REUSED: "Nothing was sent or changed by this call: this confirmToken was already used, and one approval acts once. If doing it again is really intended, call again WITHOUT confirmToken and get a new approval.",
|
|
35868
|
+
TOKEN_INVALID: "Nothing was sent or changed: this confirmToken was not issued by this server for this tool, account and target (or the server has restarted since). Call again WITHOUT confirmToken for a fresh preview and approval."
|
|
35869
|
+
};
|
|
35870
|
+
var DRAFT_CHANGED_NOTE = {
|
|
35871
|
+
"message-id-rotated": "Nothing was sent or changed: the target was edited since the user approved it (a draft's messageId or an event's version rotated), so what would happen is not what they saw.",
|
|
35872
|
+
"payload-changed": "Nothing was sent or changed: what would happen no longer matches what the user approved."
|
|
35873
|
+
};
|
|
35874
|
+
function isToolResult(value) {
|
|
35875
|
+
return Array.isArray(value.content);
|
|
35876
|
+
}
|
|
35877
|
+
function rejection2(data) {
|
|
35878
|
+
return { ...textResult({ status: "confirmation-rejected", confirmed: false, dispatched: false, ...data }), isError: true };
|
|
35879
|
+
}
|
|
35880
|
+
async function tokenConfirmation(op, fallback) {
|
|
35881
|
+
const subject = await fallback.subject();
|
|
35882
|
+
if (isToolResult(subject)) return subject;
|
|
35883
|
+
const binding = {
|
|
35884
|
+
tool: fallback.tool,
|
|
35885
|
+
account: fallback.account ?? readEnvVar("GOG_ACCOUNT") ?? "",
|
|
35886
|
+
target: subject.target,
|
|
35887
|
+
...subject.revision === void 0 ? {} : { revision: subject.revision },
|
|
35888
|
+
payloadHash: hashSendPayload(subject.payload)
|
|
35889
|
+
};
|
|
35890
|
+
const phaseOne = () => {
|
|
35891
|
+
const { token, expiresAt } = issueConfirmToken(binding);
|
|
35892
|
+
return {
|
|
35893
|
+
action: op,
|
|
35894
|
+
preview: subject.preview,
|
|
35895
|
+
confirmToken: token,
|
|
35896
|
+
expiresAt,
|
|
35897
|
+
ttlSeconds: confirmTokenTtlSeconds(),
|
|
35898
|
+
instruction: fallback.instruction ?? CONFIRM_ACTION_INSTRUCTION
|
|
35899
|
+
};
|
|
35900
|
+
};
|
|
35901
|
+
if (!fallback.confirmToken) {
|
|
35902
|
+
return textResult({ status: "confirmation-required", confirmed: false, dispatched: false, ...phaseOne() });
|
|
35903
|
+
}
|
|
35904
|
+
const verdict = verifyConfirmToken(fallback.confirmToken, binding);
|
|
35905
|
+
if (verdict.ok) return void 0;
|
|
35906
|
+
if (verdict.error === "DRAFT_CHANGED") {
|
|
35907
|
+
return rejection2({
|
|
35908
|
+
error: "DRAFT_CHANGED",
|
|
35909
|
+
reason: verdict.reason,
|
|
35910
|
+
note: `${DRAFT_CHANGED_NOTE[verdict.reason]} The current preview and a fresh confirmToken are below.`,
|
|
35911
|
+
...phaseOne()
|
|
35912
|
+
});
|
|
35913
|
+
}
|
|
35914
|
+
return rejection2({ error: verdict.error, action: op, note: TOKEN_ERROR_NOTE[verdict.error] });
|
|
35915
|
+
}
|
|
35916
|
+
function gatedElsewhere(what, via, does, tool) {
|
|
35917
|
+
return `${what} ${does} and is not available through ${via}. Use ${tool}, which asks the user to confirm.`;
|
|
35918
|
+
}
|
|
35919
|
+
function hasCommandWord(args, words) {
|
|
35920
|
+
return args.find((a) => words.has(a.toLowerCase()));
|
|
35921
|
+
}
|
|
35922
|
+
async function requireDispatchConfirmation(ctx, options) {
|
|
35923
|
+
const { action, fallback } = options;
|
|
35924
|
+
if (fallback && callerAcceptsFormElicitation(ctx) === false && sendConfirmFallbackEnabled()) {
|
|
35925
|
+
return tokenConfirmation(action, fallback);
|
|
35926
|
+
}
|
|
35927
|
+
const note = fallback ? [options.unsupportedNote, FALLBACK_HINT].filter(Boolean).join(" ") : options.unsupportedNote;
|
|
35928
|
+
return requireConfirmation(ctx, {
|
|
35929
|
+
action,
|
|
35930
|
+
message: options.message,
|
|
35931
|
+
details: options.details,
|
|
35932
|
+
confirmationLabel: options.confirmationLabel,
|
|
35933
|
+
...note ? { unsupportedNote: note } : {}
|
|
35934
|
+
});
|
|
35935
|
+
}
|
|
35936
|
+
function resultText(result) {
|
|
35937
|
+
const first = result.content[0];
|
|
35938
|
+
return first && first.type === "text" && typeof first.text === "string" ? first.text : "{}";
|
|
35939
|
+
}
|
|
35940
|
+
|
|
35254
35941
|
// ../gogcli-mcp/src/tools/auth.ts
|
|
35942
|
+
var AUTH_RUN_SUBCOMMANDS = ["list", "status", "services", "remove", "alias"];
|
|
35255
35943
|
function registerAuthToolsWith(server, defaultServices) {
|
|
35256
35944
|
const servicesDescribe = `Services to authorize: "all" or comma-separated list (e.g. "sheets,gmail,calendar"). Default: "${defaultServices}". Prefer the narrowest set you need \u2014 requesting a service whose Google API is not enabled on the OAuth client's project makes Google reject the WHOLE request with invalid_scope.`;
|
|
35257
35945
|
const extraScopesDescribe = "Additional raw OAuth scope URIs to request, comma-separated, on top of the ones `services` implies. Use for scopes no service covers \u2014 e.g. https://www.googleapis.com/auth/bigquery.readonly, required before gog_sheets_datasource_* can read BigQuery-backed Connected Sheets. Leave unset otherwise: an extra scope whose API is not enabled on the OAuth client project makes Google reject the WHOLE authorization with invalid_scope.";
|
|
@@ -35309,7 +35997,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
35309
35997
|
})
|
|
35310
35998
|
}, async ({ email: email3, services = defaultServices, extraScopes }) => {
|
|
35311
35999
|
try {
|
|
35312
|
-
const args = ["auth", "add", email3, "--services", services];
|
|
36000
|
+
const args = ["auth", "add", pos(email3), "--services", services];
|
|
35313
36001
|
if (extraScopes) args.push(`--extra-scopes=${extraScopes}`, "--force-consent");
|
|
35314
36002
|
return rawTextResult(await run(args, {
|
|
35315
36003
|
interactive: true,
|
|
@@ -35329,7 +36017,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
35329
36017
|
})
|
|
35330
36018
|
}, async ({ email: email3, services = defaultServices, extraScopes }) => {
|
|
35331
36019
|
try {
|
|
35332
|
-
const args = ["auth", "add", email3, "--remote", "--step", "1", "--services", services, "--force-consent"];
|
|
36020
|
+
const args = ["auth", "add", pos(email3), "--remote", "--step", "1", "--services", services, "--force-consent"];
|
|
35333
36021
|
if (extraScopes) args.push(`--extra-scopes=${extraScopes}`);
|
|
35334
36022
|
return rawTextResult(await run(args, { redactMode: "tokens" }));
|
|
35335
36023
|
} catch (err) {
|
|
@@ -35356,7 +36044,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
35356
36044
|
const args = [
|
|
35357
36045
|
"auth",
|
|
35358
36046
|
"add",
|
|
35359
|
-
email3,
|
|
36047
|
+
pos(email3),
|
|
35360
36048
|
"--remote",
|
|
35361
36049
|
"--step",
|
|
35362
36050
|
"2",
|
|
@@ -35376,6 +36064,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
35376
36064
|
service: "auth",
|
|
35377
36065
|
examples: '"remove", "alias", "list"',
|
|
35378
36066
|
omitAccount: true,
|
|
36067
|
+
allowedSubcommands: AUTH_RUN_SUBCOMMANDS,
|
|
35379
36068
|
note: "For browser-based authorization, use gog_auth_add instead."
|
|
35380
36069
|
});
|
|
35381
36070
|
}
|
|
@@ -35414,6 +36103,39 @@ var attachInlineParam = external_exports.array(inlineAttachmentSchema).optional(
|
|
|
35414
36103
|
);
|
|
35415
36104
|
|
|
35416
36105
|
// ../gogcli-mcp/src/tools/classroom.ts
|
|
36106
|
+
var CLASSROOM_CREATE_WORDS = /* @__PURE__ */ new Set(["create", "add", "new"]);
|
|
36107
|
+
var CLASSROOM_GATED = {
|
|
36108
|
+
announcements: { does: "posts to a class", tool: "gog_classroom_announcements_create" },
|
|
36109
|
+
announcement: { does: "posts to a class", tool: "gog_classroom_announcements_create" },
|
|
36110
|
+
ann: { does: "posts to a class", tool: "gog_classroom_announcements_create" },
|
|
36111
|
+
invitations: { does: "invites someone to a class", tool: "gog_classroom_invitations_create" },
|
|
36112
|
+
invitation: { does: "invites someone to a class", tool: "gog_classroom_invitations_create" },
|
|
36113
|
+
invites: { does: "invites someone to a class", tool: "gog_classroom_invitations_create" }
|
|
36114
|
+
};
|
|
36115
|
+
function vetClassroomRun(subcommand, args) {
|
|
36116
|
+
const sub = subcommand.toLowerCase();
|
|
36117
|
+
const gated = Object.hasOwn(CLASSROOM_GATED, sub) ? CLASSROOM_GATED[sub] : void 0;
|
|
36118
|
+
if (!gated) return void 0;
|
|
36119
|
+
const word = hasCommandWord(args, CLASSROOM_CREATE_WORDS);
|
|
36120
|
+
return word ? gatedElsewhere(`gog classroom ${sub} ${word.toLowerCase()}`, "gog_classroom_run", gated.does, gated.tool) : void 0;
|
|
36121
|
+
}
|
|
36122
|
+
async function readCourse(courseId, account, runner = runOrDiagnose) {
|
|
36123
|
+
const got = await runner(["classroom", "courses", "get", pos(courseId)], { account });
|
|
36124
|
+
if (got.isError) return { error: got };
|
|
36125
|
+
let course;
|
|
36126
|
+
try {
|
|
36127
|
+
course = JSON.parse(resultText(got))?.course;
|
|
36128
|
+
} catch {
|
|
36129
|
+
course = void 0;
|
|
36130
|
+
}
|
|
36131
|
+
return {
|
|
36132
|
+
course: {
|
|
36133
|
+
id: courseId,
|
|
36134
|
+
...typeof course?.name === "string" ? { name: course.name } : {},
|
|
36135
|
+
...typeof course?.section === "string" ? { section: course.section } : {}
|
|
36136
|
+
}
|
|
36137
|
+
};
|
|
36138
|
+
}
|
|
35417
36139
|
function registerClassroomTools(server) {
|
|
35418
36140
|
server.registerTool("gog_classroom_courses_list", {
|
|
35419
36141
|
description: "List Google Classroom courses.",
|
|
@@ -35447,7 +36169,7 @@ function registerClassroomTools(server) {
|
|
|
35447
36169
|
account: accountParam
|
|
35448
36170
|
})
|
|
35449
36171
|
}, async ({ courseId, account }) => {
|
|
35450
|
-
return runOrDiagnose(["classroom", "courses", "get", courseId], { account });
|
|
36172
|
+
return runOrDiagnose(["classroom", "courses", "get", pos(courseId)], { account });
|
|
35451
36173
|
});
|
|
35452
36174
|
server.registerTool("gog_classroom_students_list", {
|
|
35453
36175
|
description: "List students enrolled in a Google Classroom course.",
|
|
@@ -35461,7 +36183,7 @@ function registerClassroomTools(server) {
|
|
|
35461
36183
|
account: accountParam
|
|
35462
36184
|
})
|
|
35463
36185
|
}, async ({ courseId, max, pageToken, page, all, account }) => {
|
|
35464
|
-
const args = ["classroom", "students", "list", courseId];
|
|
36186
|
+
const args = ["classroom", "students", "list", pos(courseId)];
|
|
35465
36187
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
35466
36188
|
const token = resolvePageToken({ pageToken, page });
|
|
35467
36189
|
if (token) args.push(`--page=${token}`);
|
|
@@ -35477,7 +36199,7 @@ function registerClassroomTools(server) {
|
|
|
35477
36199
|
account: accountParam
|
|
35478
36200
|
})
|
|
35479
36201
|
}, async ({ courseId, userId, account }) => {
|
|
35480
|
-
return runOrDiagnose(["classroom", "students", "get", courseId, userId], { account });
|
|
36202
|
+
return runOrDiagnose(["classroom", "students", "get", pos(courseId), pos(userId)], { account });
|
|
35481
36203
|
});
|
|
35482
36204
|
server.registerTool("gog_classroom_teachers_list", {
|
|
35483
36205
|
description: "List teachers in a Google Classroom course.",
|
|
@@ -35491,7 +36213,7 @@ function registerClassroomTools(server) {
|
|
|
35491
36213
|
account: accountParam
|
|
35492
36214
|
})
|
|
35493
36215
|
}, async ({ courseId, max, pageToken, page, all, account }) => {
|
|
35494
|
-
const args = ["classroom", "teachers", "list", courseId];
|
|
36216
|
+
const args = ["classroom", "teachers", "list", pos(courseId)];
|
|
35495
36217
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
35496
36218
|
const token = resolvePageToken({ pageToken, page });
|
|
35497
36219
|
if (token) args.push(`--page=${token}`);
|
|
@@ -35507,7 +36229,7 @@ function registerClassroomTools(server) {
|
|
|
35507
36229
|
account: accountParam
|
|
35508
36230
|
})
|
|
35509
36231
|
}, async ({ courseId, userId, account }) => {
|
|
35510
|
-
return runOrDiagnose(["classroom", "teachers", "get", courseId, userId], { account });
|
|
36232
|
+
return runOrDiagnose(["classroom", "teachers", "get", pos(courseId), pos(userId)], { account });
|
|
35511
36233
|
});
|
|
35512
36234
|
server.registerTool("gog_classroom_roster", {
|
|
35513
36235
|
description: "List the full roster (students and/or teachers) of a Google Classroom course. Omit both flags to return both groups.",
|
|
@@ -35523,7 +36245,7 @@ function registerClassroomTools(server) {
|
|
|
35523
36245
|
account: accountParam
|
|
35524
36246
|
})
|
|
35525
36247
|
}, async ({ courseId, students, teachers, max, pageToken, page, all, account }) => {
|
|
35526
|
-
const args = ["classroom", "roster", courseId];
|
|
36248
|
+
const args = ["classroom", "roster", pos(courseId)];
|
|
35527
36249
|
if (students) args.push("--students");
|
|
35528
36250
|
if (teachers) args.push("--teachers");
|
|
35529
36251
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
@@ -35548,7 +36270,7 @@ function registerClassroomTools(server) {
|
|
|
35548
36270
|
account: accountParam
|
|
35549
36271
|
})
|
|
35550
36272
|
}, async ({ courseId, state, topic, orderBy, max, pageToken, page, all, scanPages, account }) => {
|
|
35551
|
-
const args = ["classroom", "coursework", "list", courseId];
|
|
36273
|
+
const args = ["classroom", "coursework", "list", pos(courseId)];
|
|
35552
36274
|
if (state) args.push(`--state=${state}`);
|
|
35553
36275
|
if (topic) args.push(`--topic=${topic}`);
|
|
35554
36276
|
if (orderBy) args.push(`--order-by=${orderBy}`);
|
|
@@ -35568,7 +36290,7 @@ function registerClassroomTools(server) {
|
|
|
35568
36290
|
account: accountParam
|
|
35569
36291
|
})
|
|
35570
36292
|
}, async ({ courseId, courseworkId, account }) => {
|
|
35571
|
-
return runOrDiagnose(["classroom", "coursework", "get", courseId, courseworkId], { account });
|
|
36293
|
+
return runOrDiagnose(["classroom", "coursework", "get", pos(courseId), pos(courseworkId)], { account });
|
|
35572
36294
|
});
|
|
35573
36295
|
server.registerTool("gog_classroom_submissions_list", {
|
|
35574
36296
|
description: "List student submissions for a coursework item.",
|
|
@@ -35586,7 +36308,7 @@ function registerClassroomTools(server) {
|
|
|
35586
36308
|
account: accountParam
|
|
35587
36309
|
})
|
|
35588
36310
|
}, async ({ courseId, courseworkId, state, late, user, max, pageToken, page, all, account }) => {
|
|
35589
|
-
const args = ["classroom", "submissions", "list", courseId, courseworkId];
|
|
36311
|
+
const args = ["classroom", "submissions", "list", pos(courseId), pos(courseworkId)];
|
|
35590
36312
|
if (state) args.push(`--state=${state}`);
|
|
35591
36313
|
if (late) args.push(`--late=${late}`);
|
|
35592
36314
|
if (user) args.push(`--user=${user}`);
|
|
@@ -35606,7 +36328,7 @@ function registerClassroomTools(server) {
|
|
|
35606
36328
|
account: accountParam
|
|
35607
36329
|
})
|
|
35608
36330
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
35609
|
-
return runOrDiagnose(["classroom", "submissions", "get", courseId, courseworkId, submissionId], { account });
|
|
36331
|
+
return runOrDiagnose(["classroom", "submissions", "get", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
35610
36332
|
});
|
|
35611
36333
|
server.registerTool("gog_classroom_submissions_grade", {
|
|
35612
36334
|
description: "Grade a student submission. Set draft and/or assigned grade values.",
|
|
@@ -35620,7 +36342,7 @@ function registerClassroomTools(server) {
|
|
|
35620
36342
|
account: accountParam
|
|
35621
36343
|
})
|
|
35622
36344
|
}, async ({ courseId, courseworkId, submissionId, draft, assigned, account }) => {
|
|
35623
|
-
const args = ["classroom", "submissions", "grade", courseId, courseworkId, submissionId];
|
|
36345
|
+
const args = ["classroom", "submissions", "grade", pos(courseId), pos(courseworkId), pos(submissionId)];
|
|
35624
36346
|
if (draft) args.push(`--draft=${draft}`);
|
|
35625
36347
|
if (assigned) args.push(`--assigned=${assigned}`);
|
|
35626
36348
|
return runOrDiagnose(args, { account });
|
|
@@ -35635,7 +36357,7 @@ function registerClassroomTools(server) {
|
|
|
35635
36357
|
account: accountParam
|
|
35636
36358
|
})
|
|
35637
36359
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
35638
|
-
return runOrDiagnose(["classroom", "submissions", "return", courseId, courseworkId, submissionId], { account });
|
|
36360
|
+
return runOrDiagnose(["classroom", "submissions", "return", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
35639
36361
|
});
|
|
35640
36362
|
server.registerTool("gog_classroom_submissions_turn_in", {
|
|
35641
36363
|
description: "Turn in a student submission (student action).",
|
|
@@ -35647,7 +36369,7 @@ function registerClassroomTools(server) {
|
|
|
35647
36369
|
account: accountParam
|
|
35648
36370
|
})
|
|
35649
36371
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
35650
|
-
return runOrDiagnose(["classroom", "submissions", "turn-in", courseId, courseworkId, submissionId], { account });
|
|
36372
|
+
return runOrDiagnose(["classroom", "submissions", "turn-in", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
35651
36373
|
});
|
|
35652
36374
|
server.registerTool("gog_classroom_submissions_reclaim", {
|
|
35653
36375
|
description: "Reclaim a turned-in submission (student action to edit a submission).",
|
|
@@ -35659,7 +36381,7 @@ function registerClassroomTools(server) {
|
|
|
35659
36381
|
account: accountParam
|
|
35660
36382
|
})
|
|
35661
36383
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
35662
|
-
return runOrDiagnose(["classroom", "submissions", "reclaim", courseId, courseworkId, submissionId], { account });
|
|
36384
|
+
return runOrDiagnose(["classroom", "submissions", "reclaim", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
35663
36385
|
});
|
|
35664
36386
|
server.registerTool("gog_classroom_announcements_list", {
|
|
35665
36387
|
description: "List announcements in a Google Classroom course.",
|
|
@@ -35675,7 +36397,7 @@ function registerClassroomTools(server) {
|
|
|
35675
36397
|
account: accountParam
|
|
35676
36398
|
})
|
|
35677
36399
|
}, async ({ courseId, state, orderBy, max, pageToken, page, all, account }) => {
|
|
35678
|
-
const args = ["classroom", "announcements", "list", courseId];
|
|
36400
|
+
const args = ["classroom", "announcements", "list", pos(courseId)];
|
|
35679
36401
|
if (state) args.push(`--state=${state}`);
|
|
35680
36402
|
if (orderBy) args.push(`--order-by=${orderBy}`);
|
|
35681
36403
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
@@ -35693,22 +36415,45 @@ function registerClassroomTools(server) {
|
|
|
35693
36415
|
account: accountParam
|
|
35694
36416
|
})
|
|
35695
36417
|
}, async ({ courseId, announcementId, account }) => {
|
|
35696
|
-
return runOrDiagnose(["classroom", "announcements", "get", courseId, announcementId], { account });
|
|
36418
|
+
return runOrDiagnose(["classroom", "announcements", "get", pos(courseId), pos(announcementId)], { account });
|
|
35697
36419
|
});
|
|
35698
36420
|
server.registerTool("gog_classroom_announcements_create", {
|
|
35699
|
-
description: "Create an announcement in a Google Classroom course.",
|
|
36421
|
+
description: "Create an announcement in a Google Classroom course. Unless state is DRAFT (which students cannot see), this reads the course and asks the MCP host to show the user a confirmation prompt with the class, the full text and when it publishes; nothing is posted unless they accept. To stage one without asking, pass state DRAFT." + CONFIRM_FALLBACK_DESCRIPTION,
|
|
35700
36422
|
annotations: { destructiveHint: true },
|
|
35701
36423
|
inputSchema: external_exports.object({
|
|
35702
36424
|
courseId: external_exports.string().describe("Course ID"),
|
|
35703
36425
|
text: external_exports.string().describe("Announcement text"),
|
|
35704
|
-
state: external_exports.enum(["PUBLISHED", "DRAFT"]).optional().describe("State"),
|
|
36426
|
+
state: external_exports.enum(["PUBLISHED", "DRAFT"]).optional().describe("State (DRAFT is visible only to teachers and needs no confirmation)"),
|
|
35705
36427
|
scheduled: external_exports.string().optional().describe("Scheduled publish time"),
|
|
35706
|
-
account: accountParam
|
|
36428
|
+
account: accountParam,
|
|
36429
|
+
confirmToken: confirmTokenParam
|
|
35707
36430
|
})
|
|
35708
|
-
}, async ({ courseId, text, state, scheduled, account }) => {
|
|
35709
|
-
const args = ["classroom", "announcements", "create", courseId, `--text=${text}`];
|
|
36431
|
+
}, async ({ courseId, text, state, scheduled, account, confirmToken }, ctx) => {
|
|
36432
|
+
const args = ["classroom", "announcements", "create", pos(courseId), `--text=${text}`];
|
|
35710
36433
|
if (state) args.push(`--state=${state}`);
|
|
35711
36434
|
if (scheduled) args.push(`--scheduled=${scheduled}`);
|
|
36435
|
+
if (state !== "DRAFT") {
|
|
36436
|
+
const read = await readCourse(courseId, account);
|
|
36437
|
+
if (read.error) return read.error;
|
|
36438
|
+
const publishes = scheduled ? `at ${scheduled}` : "immediately";
|
|
36439
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
36440
|
+
action: "classroom.announcement-create",
|
|
36441
|
+
message: "Review and confirm this Classroom announcement:",
|
|
36442
|
+
confirmationLabel: "Confirm that this announcement should be posted to the class.",
|
|
36443
|
+
details: { course: read.course, publishes, textPreview: bodyPreview(text) },
|
|
36444
|
+
unsupportedNote: "Create it with state DRAFT instead; the user can review and post it from Classroom.",
|
|
36445
|
+
fallback: {
|
|
36446
|
+
tool: "gog_classroom_announcements_create",
|
|
36447
|
+
account,
|
|
36448
|
+
confirmToken,
|
|
36449
|
+
subject: () => {
|
|
36450
|
+
const view = { course: read.course, publishes, text };
|
|
36451
|
+
return { target: courseId, payload: view, preview: view };
|
|
36452
|
+
}
|
|
36453
|
+
}
|
|
36454
|
+
});
|
|
36455
|
+
if (confirmation) return confirmation;
|
|
36456
|
+
}
|
|
35712
36457
|
return runOrDiagnose(args, { account });
|
|
35713
36458
|
});
|
|
35714
36459
|
server.registerTool("gog_classroom_topics_list", {
|
|
@@ -35723,7 +36468,7 @@ function registerClassroomTools(server) {
|
|
|
35723
36468
|
account: accountParam
|
|
35724
36469
|
})
|
|
35725
36470
|
}, async ({ courseId, max, pageToken, page, all, account }) => {
|
|
35726
|
-
const args = ["classroom", "topics", "list", courseId];
|
|
36471
|
+
const args = ["classroom", "topics", "list", pos(courseId)];
|
|
35727
36472
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
35728
36473
|
const token = resolvePageToken({ pageToken, page });
|
|
35729
36474
|
if (token) args.push(`--page=${token}`);
|
|
@@ -35739,7 +36484,7 @@ function registerClassroomTools(server) {
|
|
|
35739
36484
|
account: accountParam
|
|
35740
36485
|
})
|
|
35741
36486
|
}, async ({ courseId, topicId, account }) => {
|
|
35742
|
-
return runOrDiagnose(["classroom", "topics", "get", courseId, topicId], { account });
|
|
36487
|
+
return runOrDiagnose(["classroom", "topics", "get", pos(courseId), pos(topicId)], { account });
|
|
35743
36488
|
});
|
|
35744
36489
|
server.registerTool("gog_classroom_invitations_list", {
|
|
35745
36490
|
description: "List Google Classroom invitations.",
|
|
@@ -35771,7 +36516,7 @@ function registerClassroomTools(server) {
|
|
|
35771
36516
|
account: accountParam
|
|
35772
36517
|
})
|
|
35773
36518
|
}, async ({ invitationId, account }) => {
|
|
35774
|
-
return runOrDiagnose(["classroom", "invitations", "get", invitationId], { account });
|
|
36519
|
+
return runOrDiagnose(["classroom", "invitations", "get", pos(invitationId)], { account });
|
|
35775
36520
|
});
|
|
35776
36521
|
server.registerTool("gog_classroom_invitations_accept", {
|
|
35777
36522
|
description: "Accept a Google Classroom invitation.",
|
|
@@ -35781,7 +36526,7 @@ function registerClassroomTools(server) {
|
|
|
35781
36526
|
account: accountParam
|
|
35782
36527
|
})
|
|
35783
36528
|
}, async ({ invitationId, account }) => {
|
|
35784
|
-
return runOrDiagnose(["classroom", "invitations", "accept", invitationId], { account });
|
|
36529
|
+
return runOrDiagnose(["classroom", "invitations", "accept", pos(invitationId)], { account });
|
|
35785
36530
|
});
|
|
35786
36531
|
server.registerTool("gog_classroom_profile_get", {
|
|
35787
36532
|
description: "Get a Google Classroom user profile. Omit userId to fetch the authenticated user.",
|
|
@@ -35792,12 +36537,13 @@ function registerClassroomTools(server) {
|
|
|
35792
36537
|
})
|
|
35793
36538
|
}, async ({ userId, account }) => {
|
|
35794
36539
|
const args = ["classroom", "profile", "get"];
|
|
35795
|
-
if (userId) args.push(userId);
|
|
36540
|
+
if (userId) args.push(pos(userId));
|
|
35796
36541
|
return runOrDiagnose(args, { account });
|
|
35797
36542
|
});
|
|
35798
36543
|
registerRunTool(server, {
|
|
35799
36544
|
service: "classroom",
|
|
35800
36545
|
examples: '"guardians", "materials", "guardian-invitations"',
|
|
36546
|
+
vet: vetClassroomRun,
|
|
35801
36547
|
note: "Covers anything not wrapped by the dedicated tools (guardians, guardian-invitations, materials, coursework assignees, announcement assignees, etc.)."
|
|
35802
36548
|
});
|
|
35803
36549
|
}
|
|
@@ -35814,7 +36560,7 @@ var replySchema = {
|
|
|
35814
36560
|
remove: external_exports.array(external_exports.string()).optional().describe("Remove these recipients from all fields (repeatable) \u2014 e.g. to drop someone from a reply-all."),
|
|
35815
36561
|
subject: external_exports.string().optional().describe('Override reply subject (default: "Re: <original>"). A changed subject starts a NEW Gmail thread.'),
|
|
35816
36562
|
noQuote: external_exports.boolean().optional().describe("Do not include the original message quoted below the reply (default: the original is quoted)"),
|
|
35817
|
-
attach: external_exports.array(external_exports.string()).optional().describe(`File paths to attach (repeatable), resolved ON THE GOG SERVER's filesystem \u2014 NOT this client's. Only usable when gog runs on the same machine you do (local stdio); on a hosted deployment (e.g. mcp-host) these paths do not exist and the call fails with "no such file or directory" \u2014 use attachInline there. Read on the server, base64-encoded with a MIME type inferred from the extension.`),
|
|
36563
|
+
attach: external_exports.array(external_exports.string()).optional().describe(`File paths to attach (repeatable), resolved ON THE GOG SERVER's filesystem \u2014 NOT this client's. Only usable when gog runs on the same machine you do (local stdio); on a hosted deployment (e.g. mcp-host) these paths do not exist and the call fails with "no such file or directory" \u2014 use attachInline there. Read on the server, base64-encoded with a MIME type inferred from the extension. Must be inside the server's GOG_FILE_ROOTS directories (default ~/gogcli-mcp-files).`),
|
|
35818
36564
|
attachInline: attachInlineParam,
|
|
35819
36565
|
from: external_exports.string().optional().describe("Send from this email address (must be a verified send-as alias)"),
|
|
35820
36566
|
autoFromAddressedAlias: external_exports.boolean().optional().describe("When from is omitted, send from the verified send-as alias the original message was addressed TO, instead of the account's primary address \u2014 so a reply to mail sent to an alias goes back out from that alias. Ignored when from is set."),
|
|
@@ -35823,7 +36569,7 @@ var replySchema = {
|
|
|
35823
36569
|
signatureFile: external_exports.string().optional().describe("Append a local signature file (plain text or HTML), read on the gog server"),
|
|
35824
36570
|
account: accountParam
|
|
35825
36571
|
};
|
|
35826
|
-
var sendReplySchema = external_exports.object(replySchema);
|
|
36572
|
+
var sendReplySchema = external_exports.object({ ...replySchema, confirmToken: confirmTokenParam });
|
|
35827
36573
|
|
|
35828
36574
|
// ../gogcli-mcp/src/tools/sheets.ts
|
|
35829
36575
|
var cellValueParam = external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean(), external_exports.null()]);
|
|
@@ -35835,10 +36581,10 @@ var failIfNotEmptyParam = external_exports.boolean().optional().describe(
|
|
|
35835
36581
|
);
|
|
35836
36582
|
|
|
35837
36583
|
// ../gogcli-mcp/src/server.ts
|
|
35838
|
-
var VERSION = true ? "4.
|
|
36584
|
+
var VERSION = true ? "4.4.0" : "0.0.0";
|
|
35839
36585
|
|
|
35840
36586
|
// ../gogcli-mcp/src/bootstrap-auth.ts
|
|
35841
|
-
import { join as
|
|
36587
|
+
import { join as join5 } from "node:path";
|
|
35842
36588
|
var AUTH_BOOTSTRAP_MARKER = "auth-bootstrap.sha256";
|
|
35843
36589
|
var VARS = ["GOG_CLIENT_ID", "GOG_CLIENT_SECRET", "GOG_REFRESH_TOKEN", "GOG_ACCOUNT"];
|
|
35844
36590
|
var log = (line) => {
|
|
@@ -35872,12 +36618,12 @@ async function bootstrapGogAuth(env = process.env, options = {}) {
|
|
|
35872
36618
|
const [clientId, clientSecret, refreshToken, account] = values;
|
|
35873
36619
|
const { spawner } = options;
|
|
35874
36620
|
try {
|
|
35875
|
-
const { createHash } = await import("node:crypto");
|
|
36621
|
+
const { createHash: createHash3 } = await import("node:crypto");
|
|
35876
36622
|
const { mkdir, readFile, writeFile, chmod } = await import("node:fs/promises");
|
|
35877
36623
|
const home = options.home ?? (await import("node:os")).homedir();
|
|
35878
|
-
const dir =
|
|
35879
|
-
const marker =
|
|
35880
|
-
const fingerprint =
|
|
36624
|
+
const dir = join5(home, ".gogcli-mcp");
|
|
36625
|
+
const marker = join5(dir, AUTH_BOOTSTRAP_MARKER);
|
|
36626
|
+
const fingerprint = createHash3("sha256").update(values.join("\0")).digest("hex");
|
|
35881
36627
|
const previous = await readFile(marker, "utf8").catch(() => void 0);
|
|
35882
36628
|
if (previous === fingerprint && await accountListed(account, spawner)) return "present";
|
|
35883
36629
|
await run(
|
|
@@ -35961,7 +36707,7 @@ function registerExtraClassroomTools(server) {
|
|
|
35961
36707
|
account: accountParam
|
|
35962
36708
|
})
|
|
35963
36709
|
}, async ({ courseId, name, owner, section, descriptionHeading, description, room, state, account }) => {
|
|
35964
|
-
const args = ["classroom", "courses", "update", courseId];
|
|
36710
|
+
const args = ["classroom", "courses", "update", pos(courseId)];
|
|
35965
36711
|
if (name) args.push(`--name=${name}`);
|
|
35966
36712
|
if (owner) args.push(`--owner=${owner}`);
|
|
35967
36713
|
if (section) args.push(`--section=${section}`);
|
|
@@ -35979,7 +36725,7 @@ function registerExtraClassroomTools(server) {
|
|
|
35979
36725
|
account: accountParam
|
|
35980
36726
|
})
|
|
35981
36727
|
}, async ({ courseId, account }) => {
|
|
35982
|
-
return runOrDiagnose(["classroom", "courses", "delete", courseId, "--force"], { account });
|
|
36728
|
+
return runOrDiagnose(["classroom", "courses", "delete", pos(courseId), "--force"], { account });
|
|
35983
36729
|
});
|
|
35984
36730
|
server.registerTool("gog_classroom_courses_archive", {
|
|
35985
36731
|
description: "Archive a Google Classroom course.",
|
|
@@ -35989,7 +36735,7 @@ function registerExtraClassroomTools(server) {
|
|
|
35989
36735
|
account: accountParam
|
|
35990
36736
|
})
|
|
35991
36737
|
}, async ({ courseId, account }) => {
|
|
35992
|
-
return runOrDiagnose(["classroom", "courses", "archive", courseId], { account });
|
|
36738
|
+
return runOrDiagnose(["classroom", "courses", "archive", pos(courseId)], { account });
|
|
35993
36739
|
});
|
|
35994
36740
|
server.registerTool("gog_classroom_courses_unarchive", {
|
|
35995
36741
|
description: "Unarchive a Google Classroom course (restore to ACTIVE).",
|
|
@@ -35999,7 +36745,7 @@ function registerExtraClassroomTools(server) {
|
|
|
35999
36745
|
account: accountParam
|
|
36000
36746
|
})
|
|
36001
36747
|
}, async ({ courseId, account }) => {
|
|
36002
|
-
return runOrDiagnose(["classroom", "courses", "unarchive", courseId], { account });
|
|
36748
|
+
return runOrDiagnose(["classroom", "courses", "unarchive", pos(courseId)], { account });
|
|
36003
36749
|
});
|
|
36004
36750
|
server.registerTool("gog_classroom_students_add", {
|
|
36005
36751
|
description: "Add a student to a Google Classroom course.",
|
|
@@ -36011,7 +36757,7 @@ function registerExtraClassroomTools(server) {
|
|
|
36011
36757
|
account: accountParam
|
|
36012
36758
|
})
|
|
36013
36759
|
}, async ({ courseId, userId, enrollmentCode, account }) => {
|
|
36014
|
-
const args = ["classroom", "students", "add", courseId, userId];
|
|
36760
|
+
const args = ["classroom", "students", "add", pos(courseId), pos(userId)];
|
|
36015
36761
|
if (enrollmentCode) args.push(`--enrollment-code=${enrollmentCode}`);
|
|
36016
36762
|
return runOrDiagnose(args, { account });
|
|
36017
36763
|
});
|
|
@@ -36024,7 +36770,7 @@ function registerExtraClassroomTools(server) {
|
|
|
36024
36770
|
account: accountParam
|
|
36025
36771
|
})
|
|
36026
36772
|
}, async ({ courseId, userId, account }) => {
|
|
36027
|
-
return runOrDiagnose(["classroom", "students", "remove", courseId, userId, "--force"], { account });
|
|
36773
|
+
return runOrDiagnose(["classroom", "students", "remove", pos(courseId), pos(userId), "--force"], { account });
|
|
36028
36774
|
});
|
|
36029
36775
|
server.registerTool("gog_classroom_teachers_add", {
|
|
36030
36776
|
description: "Add a teacher to a Google Classroom course.",
|
|
@@ -36035,7 +36781,7 @@ function registerExtraClassroomTools(server) {
|
|
|
36035
36781
|
account: accountParam
|
|
36036
36782
|
})
|
|
36037
36783
|
}, async ({ courseId, userId, account }) => {
|
|
36038
|
-
return runOrDiagnose(["classroom", "teachers", "add", courseId, userId], { account });
|
|
36784
|
+
return runOrDiagnose(["classroom", "teachers", "add", pos(courseId), pos(userId)], { account });
|
|
36039
36785
|
});
|
|
36040
36786
|
server.registerTool("gog_classroom_teachers_remove", {
|
|
36041
36787
|
description: "Remove a teacher from a Google Classroom course.",
|
|
@@ -36046,7 +36792,7 @@ function registerExtraClassroomTools(server) {
|
|
|
36046
36792
|
account: accountParam
|
|
36047
36793
|
})
|
|
36048
36794
|
}, async ({ courseId, userId, account }) => {
|
|
36049
|
-
return runOrDiagnose(["classroom", "teachers", "remove", courseId, userId, "--force"], { account });
|
|
36795
|
+
return runOrDiagnose(["classroom", "teachers", "remove", pos(courseId), pos(userId), "--force"], { account });
|
|
36050
36796
|
});
|
|
36051
36797
|
server.registerTool("gog_classroom_coursework_create", {
|
|
36052
36798
|
description: "Create a new coursework item (assignment, question, etc.) in a course.",
|
|
@@ -36058,7 +36804,7 @@ function registerExtraClassroomTools(server) {
|
|
|
36058
36804
|
account: accountParam
|
|
36059
36805
|
})
|
|
36060
36806
|
}, async ({ courseId, title, description, type, state, maxPoints, due, dueDate, dueTime, scheduled, topic, account }) => {
|
|
36061
|
-
const args = ["classroom", "coursework", "create", courseId, `--title=${title}`];
|
|
36807
|
+
const args = ["classroom", "coursework", "create", pos(courseId), `--title=${title}`];
|
|
36062
36808
|
if (description) args.push(`--description=${description}`);
|
|
36063
36809
|
if (type) args.push(`--type=${type}`);
|
|
36064
36810
|
if (state) args.push(`--state=${state}`);
|
|
@@ -36081,7 +36827,7 @@ function registerExtraClassroomTools(server) {
|
|
|
36081
36827
|
account: accountParam
|
|
36082
36828
|
})
|
|
36083
36829
|
}, async ({ courseId, courseworkId, title, description, type, state, maxPoints, due, dueDate, dueTime, scheduled, topic, account }) => {
|
|
36084
|
-
const args = ["classroom", "coursework", "update", courseId, courseworkId];
|
|
36830
|
+
const args = ["classroom", "coursework", "update", pos(courseId), pos(courseworkId)];
|
|
36085
36831
|
if (title) args.push(`--title=${title}`);
|
|
36086
36832
|
if (description) args.push(`--description=${description}`);
|
|
36087
36833
|
if (type) args.push(`--type=${type}`);
|
|
@@ -36103,7 +36849,7 @@ function registerExtraClassroomTools(server) {
|
|
|
36103
36849
|
account: accountParam
|
|
36104
36850
|
})
|
|
36105
36851
|
}, async ({ courseId, courseworkId, account }) => {
|
|
36106
|
-
return runOrDiagnose(["classroom", "coursework", "delete", courseId, courseworkId, "--force"], { account });
|
|
36852
|
+
return runOrDiagnose(["classroom", "coursework", "delete", pos(courseId), pos(courseworkId), "--force"], { account });
|
|
36107
36853
|
});
|
|
36108
36854
|
server.registerTool("gog_classroom_announcements_update", {
|
|
36109
36855
|
description: "Update an existing announcement.",
|
|
@@ -36117,7 +36863,7 @@ function registerExtraClassroomTools(server) {
|
|
|
36117
36863
|
account: accountParam
|
|
36118
36864
|
})
|
|
36119
36865
|
}, async ({ courseId, announcementId, text, state, scheduled, account }) => {
|
|
36120
|
-
const args = ["classroom", "announcements", "update", courseId, announcementId];
|
|
36866
|
+
const args = ["classroom", "announcements", "update", pos(courseId), pos(announcementId)];
|
|
36121
36867
|
if (text) args.push(`--text=${text}`);
|
|
36122
36868
|
if (state) args.push(`--state=${state}`);
|
|
36123
36869
|
if (scheduled) args.push(`--scheduled=${scheduled}`);
|
|
@@ -36132,7 +36878,7 @@ function registerExtraClassroomTools(server) {
|
|
|
36132
36878
|
account: accountParam
|
|
36133
36879
|
})
|
|
36134
36880
|
}, async ({ courseId, announcementId, account }) => {
|
|
36135
|
-
return runOrDiagnose(["classroom", "announcements", "delete", courseId, announcementId, "--force"], { account });
|
|
36881
|
+
return runOrDiagnose(["classroom", "announcements", "delete", pos(courseId), pos(announcementId), "--force"], { account });
|
|
36136
36882
|
});
|
|
36137
36883
|
server.registerTool("gog_classroom_topics_create", {
|
|
36138
36884
|
description: "Create a topic in a Google Classroom course.",
|
|
@@ -36143,7 +36889,7 @@ function registerExtraClassroomTools(server) {
|
|
|
36143
36889
|
account: accountParam
|
|
36144
36890
|
})
|
|
36145
36891
|
}, async ({ courseId, name, account }) => {
|
|
36146
|
-
return runOrDiagnose(["classroom", "topics", "create", courseId, `--name=${name}`], { account });
|
|
36892
|
+
return runOrDiagnose(["classroom", "topics", "create", pos(courseId), `--name=${name}`], { account });
|
|
36147
36893
|
});
|
|
36148
36894
|
server.registerTool("gog_classroom_topics_update", {
|
|
36149
36895
|
description: "Rename an existing topic.",
|
|
@@ -36155,7 +36901,7 @@ function registerExtraClassroomTools(server) {
|
|
|
36155
36901
|
account: accountParam
|
|
36156
36902
|
})
|
|
36157
36903
|
}, async ({ courseId, topicId, name, account }) => {
|
|
36158
|
-
return runOrDiagnose(["classroom", "topics", "update", courseId, topicId, `--name=${name}`], { account });
|
|
36904
|
+
return runOrDiagnose(["classroom", "topics", "update", pos(courseId), pos(topicId), `--name=${name}`], { account });
|
|
36159
36905
|
});
|
|
36160
36906
|
server.registerTool("gog_classroom_topics_delete", {
|
|
36161
36907
|
description: "Delete a topic.",
|
|
@@ -36166,19 +36912,37 @@ function registerExtraClassroomTools(server) {
|
|
|
36166
36912
|
account: accountParam
|
|
36167
36913
|
})
|
|
36168
36914
|
}, async ({ courseId, topicId, account }) => {
|
|
36169
|
-
return runOrDiagnose(["classroom", "topics", "delete", courseId, topicId, "--force"], { account });
|
|
36915
|
+
return runOrDiagnose(["classroom", "topics", "delete", pos(courseId), pos(topicId), "--force"], { account });
|
|
36170
36916
|
});
|
|
36171
36917
|
server.registerTool("gog_classroom_invitations_create", {
|
|
36172
|
-
description: "Create an invitation to a Google Classroom course.",
|
|
36918
|
+
description: "Create an invitation to a Google Classroom course \u2014 Classroom emails the invitee. Reads the course and asks the MCP host to show the user a confirmation prompt with the class, the invitee and the role; nothing is sent unless they accept." + CONFIRM_FALLBACK_DESCRIPTION,
|
|
36173
36919
|
annotations: { destructiveHint: true },
|
|
36174
36920
|
inputSchema: external_exports.object({
|
|
36175
36921
|
courseId: external_exports.string().describe("Course ID"),
|
|
36176
36922
|
userId: external_exports.string().describe("User ID to invite"),
|
|
36177
36923
|
role: external_exports.enum(["STUDENT", "TEACHER", "OWNER"]).describe("Role for the invited user"),
|
|
36178
|
-
account: accountParam
|
|
36924
|
+
account: accountParam,
|
|
36925
|
+
confirmToken: confirmTokenParam
|
|
36179
36926
|
})
|
|
36180
|
-
}, async ({ courseId, userId, role, account }) => {
|
|
36181
|
-
|
|
36927
|
+
}, async ({ courseId, userId, role, account, confirmToken }, ctx) => {
|
|
36928
|
+
const read = await readCourse(courseId, account, runOrDiagnose);
|
|
36929
|
+
if (read.error) return read.error;
|
|
36930
|
+
const invitation = { course: read.course, invitee: userId, role };
|
|
36931
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
36932
|
+
action: "classroom.invitation-create",
|
|
36933
|
+
message: role === "OWNER" ? "Review and confirm this Classroom invitation \u2014 it offers OWNERSHIP of the course:" : "Review and confirm this Classroom invitation:",
|
|
36934
|
+
confirmationLabel: "Confirm that this invitation should be sent now.",
|
|
36935
|
+
details: invitation,
|
|
36936
|
+
unsupportedNote: "Ask the user to invite them from Classroom.",
|
|
36937
|
+
fallback: {
|
|
36938
|
+
tool: "gog_classroom_invitations_create",
|
|
36939
|
+
account,
|
|
36940
|
+
confirmToken,
|
|
36941
|
+
subject: () => ({ target: `${courseId}/${userId}`, payload: invitation, preview: invitation })
|
|
36942
|
+
}
|
|
36943
|
+
});
|
|
36944
|
+
if (confirmation) return confirmation;
|
|
36945
|
+
return runOrDiagnose(["classroom", "invitations", "create", pos(courseId), pos(userId), `--role=${role}`], { account });
|
|
36182
36946
|
});
|
|
36183
36947
|
server.registerTool("gog_classroom_invitations_delete", {
|
|
36184
36948
|
description: "Delete (revoke) a Google Classroom invitation.",
|
|
@@ -36188,7 +36952,7 @@ function registerExtraClassroomTools(server) {
|
|
|
36188
36952
|
account: accountParam
|
|
36189
36953
|
})
|
|
36190
36954
|
}, async ({ invitationId, account }) => {
|
|
36191
|
-
return runOrDiagnose(["classroom", "invitations", "delete", invitationId, "--force"], { account });
|
|
36955
|
+
return runOrDiagnose(["classroom", "invitations", "delete", pos(invitationId), "--force"], { account });
|
|
36192
36956
|
});
|
|
36193
36957
|
}
|
|
36194
36958
|
|