gogcli-mcp-calendar 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/dist/index.js +927 -121
- package/manifest.json +9 -2
- package/package.json +2 -2
- package/src/tools/calendar-extra.ts +22 -21
- package/tests/tools/calendar-extra.test.ts +29 -28
- 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
|
|
@@ -34464,6 +34537,11 @@ function walk(value, keep, drop) {
|
|
|
34464
34537
|
}
|
|
34465
34538
|
|
|
34466
34539
|
// ../../node_modules/@chrischall/mcp-utils/dist/response/index.js
|
|
34540
|
+
function textResult(data) {
|
|
34541
|
+
return {
|
|
34542
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
34543
|
+
};
|
|
34544
|
+
}
|
|
34467
34545
|
function rawTextResult(text) {
|
|
34468
34546
|
return { content: [{ type: "text", text }] };
|
|
34469
34547
|
}
|
|
@@ -34474,6 +34552,154 @@ function errorResult(message) {
|
|
|
34474
34552
|
};
|
|
34475
34553
|
}
|
|
34476
34554
|
|
|
34555
|
+
// ../../node_modules/@chrischall/mcp-utils/dist/server/confirmation.js
|
|
34556
|
+
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
34557
|
+
var DEFAULT_REQUEST_KEY = "confirmation";
|
|
34558
|
+
var DEFAULT_CONFIRMATION_LABEL = "Confirm this action should proceed.";
|
|
34559
|
+
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";
|
|
34560
|
+
var STATE_PREFIX = "mcpu.confirm.v1.";
|
|
34561
|
+
function canonicalJson(value) {
|
|
34562
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
34563
|
+
return JSON.stringify(value);
|
|
34564
|
+
if (value === void 0)
|
|
34565
|
+
return "null";
|
|
34566
|
+
if (typeof value === "number")
|
|
34567
|
+
return Number.isFinite(value) ? JSON.stringify(value) : `{"$num":"${String(value)}"}`;
|
|
34568
|
+
if (typeof value === "bigint")
|
|
34569
|
+
return `{"$bigint":"${value.toString()}"}`;
|
|
34570
|
+
if (typeof value === "function" || typeof value === "symbol") {
|
|
34571
|
+
throw new TypeError(`requireConfirmation: cannot bind a ${typeof value} argument value.`);
|
|
34572
|
+
}
|
|
34573
|
+
if (Array.isArray(value))
|
|
34574
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
34575
|
+
if (value instanceof Date) {
|
|
34576
|
+
return `{"$date":${JSON.stringify(Number.isNaN(value.getTime()) ? "Invalid Date" : value.toISOString())}}`;
|
|
34577
|
+
}
|
|
34578
|
+
if (ArrayBuffer.isView(value)) {
|
|
34579
|
+
const bytes = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
34580
|
+
return `{"$bytes":${JSON.stringify(bytes.toString("base64"))}}`;
|
|
34581
|
+
}
|
|
34582
|
+
if (value instanceof ArrayBuffer)
|
|
34583
|
+
return `{"$bytes":${JSON.stringify(Buffer.from(value).toString("base64"))}}`;
|
|
34584
|
+
if (value instanceof Map) {
|
|
34585
|
+
const entries2 = [...value.entries()].map(([k, v]) => `[${canonicalJson(k)},${canonicalJson(v)}]`).sort();
|
|
34586
|
+
return `{"$map":[${entries2.join(",")}]}`;
|
|
34587
|
+
}
|
|
34588
|
+
if (value instanceof Set) {
|
|
34589
|
+
return `{"$set":[${[...value].map(canonicalJson).sort().join(",")}]}`;
|
|
34590
|
+
}
|
|
34591
|
+
const proto = Object.getPrototypeOf(value);
|
|
34592
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
34593
|
+
throw new TypeError("requireConfirmation: cannot bind a non-plain object argument value (class instance).");
|
|
34594
|
+
}
|
|
34595
|
+
const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
34596
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(escapeKey(k))}:${canonicalJson(v)}`).join(",")}}`;
|
|
34597
|
+
}
|
|
34598
|
+
function escapeKey(key) {
|
|
34599
|
+
return key.startsWith("$") ? `$${key}` : key;
|
|
34600
|
+
}
|
|
34601
|
+
function bindingKey(binding) {
|
|
34602
|
+
if (binding.ttlSeconds !== void 0 && !(Number.isFinite(binding.ttlSeconds) && binding.ttlSeconds > 0)) {
|
|
34603
|
+
throw new RangeError("requireConfirmation: binding.ttlSeconds must be a finite number greater than 0.");
|
|
34604
|
+
}
|
|
34605
|
+
const key = typeof binding.key === "string" ? Buffer.from(binding.key, "utf8") : Buffer.from(binding.key);
|
|
34606
|
+
if (key.length < 32) {
|
|
34607
|
+
throw new RangeError("requireConfirmation: binding.key must be at least 32 bytes.");
|
|
34608
|
+
}
|
|
34609
|
+
return key;
|
|
34610
|
+
}
|
|
34611
|
+
function commitment(action, args) {
|
|
34612
|
+
return createHash("sha256").update(`${action}\0${canonicalJson(args)}`).digest("base64url");
|
|
34613
|
+
}
|
|
34614
|
+
function mintState(key, action, binding) {
|
|
34615
|
+
const exp = Math.floor(Date.now() / 1e3) + (binding.ttlSeconds ?? 600);
|
|
34616
|
+
const body = Buffer.from(JSON.stringify({ c: commitment(action, binding.args), exp })).toString("base64url");
|
|
34617
|
+
const mac3 = createHmac("sha256", key).update(`${STATE_PREFIX}${body}`).digest("base64url");
|
|
34618
|
+
return `${STATE_PREFIX}${body}.${mac3}`;
|
|
34619
|
+
}
|
|
34620
|
+
function verifyState(key, action, binding, state) {
|
|
34621
|
+
if (typeof state !== "string" || !state.startsWith(STATE_PREFIX))
|
|
34622
|
+
return false;
|
|
34623
|
+
const rest = state.slice(STATE_PREFIX.length);
|
|
34624
|
+
const dot = rest.indexOf(".");
|
|
34625
|
+
if (dot < 0)
|
|
34626
|
+
return false;
|
|
34627
|
+
const body = rest.slice(0, dot);
|
|
34628
|
+
const mac3 = Buffer.from(rest.slice(dot + 1), "base64url");
|
|
34629
|
+
const expected = createHmac("sha256", key).update(`${STATE_PREFIX}${body}`).digest();
|
|
34630
|
+
if (mac3.length !== expected.length || !timingSafeEqual(mac3, expected))
|
|
34631
|
+
return false;
|
|
34632
|
+
let payload;
|
|
34633
|
+
try {
|
|
34634
|
+
payload = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
|
|
34635
|
+
} catch {
|
|
34636
|
+
return false;
|
|
34637
|
+
}
|
|
34638
|
+
if (typeof payload.exp !== "number" || payload.exp * 1e3 <= Date.now())
|
|
34639
|
+
return false;
|
|
34640
|
+
const want = Buffer.from(commitment(action, binding.args));
|
|
34641
|
+
const got = Buffer.from(typeof payload.c === "string" ? payload.c : "");
|
|
34642
|
+
return got.length === want.length && timingSafeEqual(got, want);
|
|
34643
|
+
}
|
|
34644
|
+
function echoedState(ctx) {
|
|
34645
|
+
const accessor = ctx.mcpReq.requestState;
|
|
34646
|
+
return typeof accessor === "function" ? accessor() : void 0;
|
|
34647
|
+
}
|
|
34648
|
+
function requireConfirmation(ctx, options) {
|
|
34649
|
+
const requestKey = options.requestKey ?? DEFAULT_REQUEST_KEY;
|
|
34650
|
+
const key = options.binding ? bindingKey(options.binding) : void 0;
|
|
34651
|
+
const confirmationSchema = external_exports.object({
|
|
34652
|
+
confirmed: external_exports.boolean().describe(options.confirmationLabel ?? DEFAULT_CONFIRMATION_LABEL)
|
|
34653
|
+
});
|
|
34654
|
+
const ask = () => {
|
|
34655
|
+
const preview = {
|
|
34656
|
+
action: options.action,
|
|
34657
|
+
...options.details === void 0 ? {} : { details: options.details }
|
|
34658
|
+
};
|
|
34659
|
+
return inputRequired({
|
|
34660
|
+
inputRequests: {
|
|
34661
|
+
[requestKey]: inputRequired.elicit({
|
|
34662
|
+
message: `${options.message}
|
|
34663
|
+
${JSON.stringify(preview, null, 2)}`,
|
|
34664
|
+
requestedSchema: confirmationSchema
|
|
34665
|
+
})
|
|
34666
|
+
},
|
|
34667
|
+
...key && options.binding ? { requestState: mintState(key, options.action, options.binding) } : {}
|
|
34668
|
+
});
|
|
34669
|
+
};
|
|
34670
|
+
const response = inputResponse(ctx.mcpReq.inputResponses, requestKey);
|
|
34671
|
+
const accepted = acceptedContent(ctx.mcpReq.inputResponses, requestKey, confirmationSchema);
|
|
34672
|
+
if (response.kind === "missing") {
|
|
34673
|
+
if (callerAcceptsFormElicitation(ctx) === false) {
|
|
34674
|
+
return textResult({
|
|
34675
|
+
confirmed: false,
|
|
34676
|
+
dispatched: false,
|
|
34677
|
+
action: options.action,
|
|
34678
|
+
reason: "confirmation-unsupported",
|
|
34679
|
+
note: options.unsupportedNote ? `${UNSUPPORTED_NOTE}. ${options.unsupportedNote}` : `${UNSUPPORTED_NOTE}.`
|
|
34680
|
+
});
|
|
34681
|
+
}
|
|
34682
|
+
return ask();
|
|
34683
|
+
}
|
|
34684
|
+
if (response.kind === "elicit" && response.action === "accept" && accepted?.confirmed === true) {
|
|
34685
|
+
if (key && options.binding) {
|
|
34686
|
+
const state = echoedState(ctx);
|
|
34687
|
+
if (state === void 0 || state === null) {
|
|
34688
|
+
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.`);
|
|
34689
|
+
}
|
|
34690
|
+
if (!verifyState(key, options.action, options.binding, state))
|
|
34691
|
+
return ask();
|
|
34692
|
+
}
|
|
34693
|
+
return void 0;
|
|
34694
|
+
}
|
|
34695
|
+
return textResult({
|
|
34696
|
+
confirmed: false,
|
|
34697
|
+
cancelled: true,
|
|
34698
|
+
action: options.action,
|
|
34699
|
+
note: "Nothing was changed because the confirmation was declined, cancelled, or left unchecked."
|
|
34700
|
+
});
|
|
34701
|
+
}
|
|
34702
|
+
|
|
34477
34703
|
// ../../node_modules/@chrischall/mcp-utils/dist/server/index.js
|
|
34478
34704
|
var SERVER_PROTOCOL_VERSIONS = Object.freeze([
|
|
34479
34705
|
"2026-07-28",
|
|
@@ -34568,13 +34794,47 @@ async function createMcpServer(opts) {
|
|
|
34568
34794
|
}
|
|
34569
34795
|
return server;
|
|
34570
34796
|
}
|
|
34797
|
+
var DEFAULT_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
34798
|
+
var DEFAULT_REPEAT_SIGNAL_GRACE_MS = 500;
|
|
34571
34799
|
function withGracefulShutdown(target, opts = {}) {
|
|
34572
34800
|
const shouldExit = opts.exit ?? true;
|
|
34801
|
+
if (opts.timeoutMs !== void 0 && !(Number.isFinite(opts.timeoutMs) && opts.timeoutMs > 0)) {
|
|
34802
|
+
throw new RangeError("withGracefulShutdown: timeoutMs must be a finite number greater than 0.");
|
|
34803
|
+
}
|
|
34804
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS;
|
|
34805
|
+
if (opts.repeatSignalGraceMs !== void 0 && !(Number.isFinite(opts.repeatSignalGraceMs) && opts.repeatSignalGraceMs >= 0)) {
|
|
34806
|
+
throw new RangeError("withGracefulShutdown: repeatSignalGraceMs must be a finite number >= 0.");
|
|
34807
|
+
}
|
|
34808
|
+
const repeatGraceMs = opts.repeatSignalGraceMs ?? DEFAULT_REPEAT_SIGNAL_GRACE_MS;
|
|
34573
34809
|
let shuttingDown = false;
|
|
34810
|
+
let firstSignalAt = 0;
|
|
34811
|
+
let exited = false;
|
|
34812
|
+
const exitOnce = () => {
|
|
34813
|
+
if (exited)
|
|
34814
|
+
return;
|
|
34815
|
+
exited = true;
|
|
34816
|
+
process.exit(0);
|
|
34817
|
+
};
|
|
34574
34818
|
const handler = (signal) => {
|
|
34575
|
-
if (shuttingDown)
|
|
34819
|
+
if (shuttingDown) {
|
|
34820
|
+
if (Date.now() - firstSignalAt < repeatGraceMs)
|
|
34821
|
+
return;
|
|
34822
|
+
if (shouldExit) {
|
|
34823
|
+
console.error(`[mcp-utils] second ${signal} during shutdown \u2014 exiting now`);
|
|
34824
|
+
exitOnce();
|
|
34825
|
+
}
|
|
34576
34826
|
return;
|
|
34827
|
+
}
|
|
34577
34828
|
shuttingDown = true;
|
|
34829
|
+
firstSignalAt = Date.now();
|
|
34830
|
+
let timer;
|
|
34831
|
+
if (shouldExit) {
|
|
34832
|
+
timer = setTimeout(() => {
|
|
34833
|
+
console.error(`[mcp-utils] graceful shutdown on ${signal} did not finish in ${timeoutMs}ms \u2014 exiting`);
|
|
34834
|
+
exitOnce();
|
|
34835
|
+
}, timeoutMs);
|
|
34836
|
+
timer.unref?.();
|
|
34837
|
+
}
|
|
34578
34838
|
void (async () => {
|
|
34579
34839
|
try {
|
|
34580
34840
|
if (opts.onSignal)
|
|
@@ -34583,8 +34843,10 @@ function withGracefulShutdown(target, opts = {}) {
|
|
|
34583
34843
|
} catch (err) {
|
|
34584
34844
|
console.error(`[mcp-utils] error during graceful shutdown on ${signal}: ${err instanceof Error ? err.message : String(err)}`);
|
|
34585
34845
|
} finally {
|
|
34846
|
+
if (timer !== void 0)
|
|
34847
|
+
clearTimeout(timer);
|
|
34586
34848
|
if (shouldExit)
|
|
34587
|
-
|
|
34849
|
+
exitOnce();
|
|
34588
34850
|
}
|
|
34589
34851
|
})();
|
|
34590
34852
|
};
|
|
@@ -34614,6 +34876,8 @@ function runMcp(opts) {
|
|
|
34614
34876
|
}
|
|
34615
34877
|
|
|
34616
34878
|
// ../../node_modules/@chrischall/mcp-utils/dist/config/index.js
|
|
34879
|
+
import { homedir } from "node:os";
|
|
34880
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
34617
34881
|
var PLACEHOLDER_RE = /^\$\{[^}]*\}$/;
|
|
34618
34882
|
function readEnvVar(key, opts = {}) {
|
|
34619
34883
|
const env = opts.env ?? process.env;
|
|
@@ -34640,6 +34904,50 @@ function parseBoolEnv(key, opts = {}) {
|
|
|
34640
34904
|
return false;
|
|
34641
34905
|
return fallback;
|
|
34642
34906
|
}
|
|
34907
|
+
function expandPath(p) {
|
|
34908
|
+
let expanded = p;
|
|
34909
|
+
if (p === "~") {
|
|
34910
|
+
expanded = homedir();
|
|
34911
|
+
} else if (p.startsWith("~/")) {
|
|
34912
|
+
expanded = join(homedir(), p.slice(2));
|
|
34913
|
+
}
|
|
34914
|
+
return isAbsolute(expanded) ? expanded : resolve(expanded);
|
|
34915
|
+
}
|
|
34916
|
+
|
|
34917
|
+
// ../../node_modules/@chrischall/mcp-utils/dist/fs/confine.js
|
|
34918
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
34919
|
+
import { dirname, isAbsolute as isAbsolute2, relative, resolve as resolve2, sep } from "node:path";
|
|
34920
|
+
function isInside(root, target) {
|
|
34921
|
+
const rel = relative(root, target);
|
|
34922
|
+
return rel === "" || !rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute2(rel);
|
|
34923
|
+
}
|
|
34924
|
+
function realPathAllowingMissing(p) {
|
|
34925
|
+
const abs = resolve2(p);
|
|
34926
|
+
let existing = abs;
|
|
34927
|
+
const tail = [];
|
|
34928
|
+
while (!existsSync(existing)) {
|
|
34929
|
+
const parent = dirname(existing);
|
|
34930
|
+
if (parent === existing)
|
|
34931
|
+
break;
|
|
34932
|
+
tail.unshift(existing.slice(parent.length).replace(/^[/\\]+/, ""));
|
|
34933
|
+
existing = parent;
|
|
34934
|
+
}
|
|
34935
|
+
let real;
|
|
34936
|
+
try {
|
|
34937
|
+
real = realpathSync(existing);
|
|
34938
|
+
} catch {
|
|
34939
|
+
real = existing;
|
|
34940
|
+
}
|
|
34941
|
+
return tail.length > 0 ? resolve2(real, ...tail) : real;
|
|
34942
|
+
}
|
|
34943
|
+
function assertPathWithinRoots(path, roots) {
|
|
34944
|
+
const target = realPathAllowingMissing(expandPath(path));
|
|
34945
|
+
for (const root of roots) {
|
|
34946
|
+
if (isInside(realPathAllowingMissing(expandPath(root)), target))
|
|
34947
|
+
return target;
|
|
34948
|
+
}
|
|
34949
|
+
throw new Error(`Path is outside the allowed directories: ${path}`);
|
|
34950
|
+
}
|
|
34643
34951
|
|
|
34644
34952
|
// ../../node_modules/@chrischall/mcp-utils/dist/zod/index.js
|
|
34645
34953
|
var PositiveInt = external_exports.number().int().positive();
|
|
@@ -34661,7 +34969,7 @@ var pageSchema = {
|
|
|
34661
34969
|
};
|
|
34662
34970
|
|
|
34663
34971
|
// ../gogcli-mcp/src/runner.ts
|
|
34664
|
-
import { delimiter, join } from "node:path";
|
|
34972
|
+
import { delimiter, join as join2 } from "node:path";
|
|
34665
34973
|
|
|
34666
34974
|
// ../gogcli-mcp/src/timestamps.ts
|
|
34667
34975
|
var DEFAULT_DISPLAY_TZ = "America/New_York";
|
|
@@ -34687,9 +34995,9 @@ function offsetAt(instant, tz) {
|
|
|
34687
34995
|
const w = wallPartsIn(instant, tz);
|
|
34688
34996
|
const asUTC = Date.UTC(w.year, w.month - 1, w.day, w.hour, w.minute, w.second, instant.getUTCMilliseconds());
|
|
34689
34997
|
const minutes = Math.round((asUTC - instant.getTime()) / 6e4);
|
|
34690
|
-
const
|
|
34998
|
+
const sign2 = minutes < 0 ? "-" : "+";
|
|
34691
34999
|
const abs = Math.abs(minutes);
|
|
34692
|
-
return `${
|
|
35000
|
+
return `${sign2}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`;
|
|
34693
35001
|
}
|
|
34694
35002
|
function wallPartsIn(instant, tz) {
|
|
34695
35003
|
const parts = new Intl.DateTimeFormat("en-US", {
|
|
@@ -34862,9 +35170,48 @@ function normalizeTimestamps(text, tz = displayTimeZone(), naiveTz = naiveSource
|
|
|
34862
35170
|
return JSON.stringify(parsed, null, detectIndent(text));
|
|
34863
35171
|
}
|
|
34864
35172
|
|
|
35173
|
+
// ../gogcli-mcp/src/arg-guard.ts
|
|
35174
|
+
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;
|
|
35175
|
+
var SHORT_ACCOUNT_CLUSTER = /^-(?!-)[A-Za-z]*a/;
|
|
35176
|
+
function forbiddenArgReason(arg) {
|
|
35177
|
+
if (arg === "--") {
|
|
35178
|
+
return 'The argument "--" is not allowed: it ends flag parsing and would disable safety flags appended after it.';
|
|
35179
|
+
}
|
|
35180
|
+
const match = FORBIDDEN_LONG_FLAG.exec(arg);
|
|
35181
|
+
if (match) {
|
|
35182
|
+
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.`;
|
|
35183
|
+
}
|
|
35184
|
+
if (SHORT_ACCOUNT_CLUSTER.test(arg)) {
|
|
35185
|
+
return `The flag ${arg} is not allowed here: -a selects the account. Use the tool's account parameter instead.`;
|
|
35186
|
+
}
|
|
35187
|
+
return void 0;
|
|
35188
|
+
}
|
|
35189
|
+
function assertSafeForwardedArgs(args) {
|
|
35190
|
+
for (const arg of args) {
|
|
35191
|
+
const reason = forbiddenArgReason(arg);
|
|
35192
|
+
if (reason) throw new Error(reason);
|
|
35193
|
+
}
|
|
35194
|
+
}
|
|
35195
|
+
var SUBCOMMAND_SHAPE = /^[a-z][a-z0-9-]*$/;
|
|
35196
|
+
function assertSafeSubcommand(subcommand) {
|
|
35197
|
+
if (!SUBCOMMAND_SHAPE.test(subcommand)) {
|
|
35198
|
+
throw new Error(
|
|
35199
|
+
`Invalid subcommand ${JSON.stringify(subcommand)}: pass a single gog subcommand name such as "list" or "mark-read", and put its arguments in args.`
|
|
35200
|
+
);
|
|
35201
|
+
}
|
|
35202
|
+
}
|
|
35203
|
+
|
|
35204
|
+
// ../gogcli-mcp/src/argv.ts
|
|
35205
|
+
function pos(value) {
|
|
35206
|
+
return { kind: "positional", value };
|
|
35207
|
+
}
|
|
35208
|
+
function isGogPositional(arg) {
|
|
35209
|
+
return typeof arg === "object" && arg !== null && arg.kind === "positional";
|
|
35210
|
+
}
|
|
35211
|
+
|
|
34865
35212
|
// ../gogcli-mcp/src/runner.ts
|
|
34866
35213
|
function isGogFileArg(arg) {
|
|
34867
|
-
return typeof arg !== "string";
|
|
35214
|
+
return typeof arg !== "string" && arg.kind === "file";
|
|
34868
35215
|
}
|
|
34869
35216
|
var TIMEOUT_MS = 3e4;
|
|
34870
35217
|
function readonlyEnvEnabled() {
|
|
@@ -34948,8 +35295,8 @@ function formatTimeout(ms) {
|
|
|
34948
35295
|
}
|
|
34949
35296
|
async function spawnWithTempFiles(args, opts) {
|
|
34950
35297
|
const { mkdtemp, mkdir, writeFile, rm } = await import("node:fs/promises");
|
|
34951
|
-
const { tmpdir } = await import("node:os");
|
|
34952
|
-
const dir = await mkdtemp(
|
|
35298
|
+
const { tmpdir: tmpdir2 } = await import("node:os");
|
|
35299
|
+
const dir = await mkdtemp(join2(tmpdir2(), "gogcli-mcp-"));
|
|
34953
35300
|
try {
|
|
34954
35301
|
const argv = [];
|
|
34955
35302
|
let seq = 0;
|
|
@@ -34958,10 +35305,10 @@ async function spawnWithTempFiles(args, opts) {
|
|
|
34958
35305
|
argv.push(arg);
|
|
34959
35306
|
continue;
|
|
34960
35307
|
}
|
|
34961
|
-
const sub =
|
|
35308
|
+
const sub = join2(dir, String(seq));
|
|
34962
35309
|
seq += 1;
|
|
34963
35310
|
await mkdir(sub, { recursive: true, mode: 448 });
|
|
34964
|
-
const path =
|
|
35311
|
+
const path = join2(sub, arg.filename ?? `${arg.flag}.${arg.ext ?? "txt"}`);
|
|
34965
35312
|
const data = arg.encoding === "base64" ? Buffer.from(arg.contents, "base64") : Buffer.from(arg.contents, "utf8");
|
|
34966
35313
|
await writeFile(path, data, { mode: 384 });
|
|
34967
35314
|
argv.push(arg.positional ? path : `--${arg.flag}=${path}`);
|
|
@@ -34979,10 +35326,10 @@ function spawnExecutor(args, opts) {
|
|
|
34979
35326
|
return spawnGog(args, opts);
|
|
34980
35327
|
}
|
|
34981
35328
|
async function spawnGog(fullArgs, opts) {
|
|
34982
|
-
const { timeout, interactive = false, spawner, binary = false } = opts;
|
|
35329
|
+
const { timeout, interactive = false, spawner, binary = false, maxOutputBytes } = opts;
|
|
34983
35330
|
const spawn = spawner ?? (await import("node:child_process")).spawn;
|
|
34984
35331
|
const effectiveTimeout = timeout ?? TIMEOUT_MS;
|
|
34985
|
-
return new Promise((
|
|
35332
|
+
return new Promise((resolve4, reject) => {
|
|
34986
35333
|
const childEnv = { ...sanitizedEnv(), GOG_TIMEZONE: naiveSourceTimeZone(), PATH: augmentedPath() };
|
|
34987
35334
|
const child = spawn(readEnvVar("GOG_PATH") ?? "gog", fullArgs, { env: childEnv });
|
|
34988
35335
|
const stdoutChunks = [];
|
|
@@ -35000,7 +35347,17 @@ async function spawnGog(fullArgs, opts) {
|
|
|
35000
35347
|
child.kill();
|
|
35001
35348
|
reject(new Error(`gog timed out after ${formatTimeout(effectiveTimeout)}`));
|
|
35002
35349
|
}, effectiveTimeout);
|
|
35350
|
+
let stdoutBytes = 0;
|
|
35003
35351
|
child.stdout.on("data", (chunk) => {
|
|
35352
|
+
if (settled) return;
|
|
35353
|
+
stdoutBytes += chunk.length;
|
|
35354
|
+
if (maxOutputBytes !== void 0 && stdoutBytes > maxOutputBytes) {
|
|
35355
|
+
settled = true;
|
|
35356
|
+
stopWatching();
|
|
35357
|
+
child.kill();
|
|
35358
|
+
reject(new Error(`gog output exceeded the limit: more than ${maxOutputBytes} bytes`));
|
|
35359
|
+
return;
|
|
35360
|
+
}
|
|
35004
35361
|
stdoutChunks.push(chunk);
|
|
35005
35362
|
});
|
|
35006
35363
|
child.stderr.on("data", (chunk) => {
|
|
@@ -35017,14 +35374,14 @@ async function spawnGog(fullArgs, opts) {
|
|
|
35017
35374
|
const stderr = Buffer.concat(stderrChunks).toString().trim();
|
|
35018
35375
|
if (code === 0) {
|
|
35019
35376
|
if (binary) {
|
|
35020
|
-
|
|
35377
|
+
resolve4(Buffer.concat(stdoutChunks).toString("base64"));
|
|
35021
35378
|
return;
|
|
35022
35379
|
}
|
|
35023
35380
|
const stdout = Buffer.concat(stdoutChunks).toString();
|
|
35024
35381
|
if (interactive && stderr) {
|
|
35025
|
-
|
|
35382
|
+
resolve4(stdout + "\n" + stderr);
|
|
35026
35383
|
} else {
|
|
35027
|
-
|
|
35384
|
+
resolve4(stdout);
|
|
35028
35385
|
}
|
|
35029
35386
|
} else {
|
|
35030
35387
|
reject(new Error(stderr || `gog exited with code ${code}`));
|
|
@@ -35044,7 +35401,33 @@ async function spawnGog(fullArgs, opts) {
|
|
|
35044
35401
|
});
|
|
35045
35402
|
});
|
|
35046
35403
|
}
|
|
35404
|
+
function placePositionals(args) {
|
|
35405
|
+
const flags = [];
|
|
35406
|
+
const positionals = [];
|
|
35407
|
+
let afterSeparator = false;
|
|
35408
|
+
for (const arg of args) {
|
|
35409
|
+
if (arg === "--") {
|
|
35410
|
+
afterSeparator = true;
|
|
35411
|
+
} else if (isGogPositional(arg)) {
|
|
35412
|
+
positionals.push(arg.value);
|
|
35413
|
+
} else if (afterSeparator || isGogFileArg(arg) && arg.positional) {
|
|
35414
|
+
positionals.push(arg);
|
|
35415
|
+
} else {
|
|
35416
|
+
flags.push(arg);
|
|
35417
|
+
}
|
|
35418
|
+
}
|
|
35419
|
+
return { flags, positionals };
|
|
35420
|
+
}
|
|
35421
|
+
function assertNoSafetyOverrides(flags) {
|
|
35422
|
+
for (const arg of flags) {
|
|
35423
|
+
if (typeof arg !== "string") continue;
|
|
35424
|
+
const reason = forbiddenArgReason(arg);
|
|
35425
|
+
if (reason) throw new Error(reason);
|
|
35426
|
+
}
|
|
35427
|
+
}
|
|
35047
35428
|
function assembleArgs(args, opts) {
|
|
35429
|
+
const { flags, positionals } = placePositionals(args);
|
|
35430
|
+
assertNoSafetyOverrides(flags);
|
|
35048
35431
|
const effectiveAccount = opts.account ?? readEnvVar("GOG_ACCOUNT");
|
|
35049
35432
|
const fullArgs = ["--json", "--color=never"];
|
|
35050
35433
|
if (!opts.interactive) {
|
|
@@ -35053,17 +35436,21 @@ function assembleArgs(args, opts) {
|
|
|
35053
35436
|
if (opts.readonly || readonlyEnvEnabled()) {
|
|
35054
35437
|
fullArgs.push("--readonly");
|
|
35055
35438
|
}
|
|
35439
|
+
if (opts.gmailNoSend) {
|
|
35440
|
+
fullArgs.push("--gmail-no-send");
|
|
35441
|
+
}
|
|
35056
35442
|
if (effectiveAccount) {
|
|
35057
35443
|
fullArgs.push("--account", effectiveAccount);
|
|
35058
35444
|
}
|
|
35059
|
-
fullArgs.push(...
|
|
35445
|
+
fullArgs.push(...flags);
|
|
35446
|
+
if (positionals.length > 0) fullArgs.push("--", ...positionals);
|
|
35060
35447
|
return fullArgs;
|
|
35061
35448
|
}
|
|
35062
35449
|
async function run(args, options = {}) {
|
|
35063
|
-
const { account, spawner, interactive = false, timeout, readonly: readonly2 = false, redactMode = "full", opaqueFields } = options;
|
|
35450
|
+
const { account, spawner, interactive = false, timeout, readonly: readonly2 = false, redactMode = "full", opaqueFields, gmailNoSend = false } = options;
|
|
35064
35451
|
const base = redactMode === "tokens" ? redactGoogleTokens : redactSecrets2;
|
|
35065
35452
|
const redact = opaqueFields?.length ? (text) => redactPreservingOpaqueFields(text, opaqueFields, base) : base;
|
|
35066
|
-
const fullArgs = assembleArgs(args, { account, interactive, readonly: readonly2 });
|
|
35453
|
+
const fullArgs = assembleArgs(args, { account, interactive, readonly: readonly2, gmailNoSend });
|
|
35067
35454
|
try {
|
|
35068
35455
|
return redact(await spawnExecutor(fullArgs, { timeout, interactive, spawner }));
|
|
35069
35456
|
} catch (err) {
|
|
@@ -35120,6 +35507,107 @@ function annotateTruncatedList(result, itemsKey) {
|
|
|
35120
35507
|
return rawTextResult(JSON.stringify(out));
|
|
35121
35508
|
}
|
|
35122
35509
|
|
|
35510
|
+
// ../gogcli-mcp/src/file-roots.ts
|
|
35511
|
+
import { homedir as homedir2 } from "node:os";
|
|
35512
|
+
import { delimiter as delimiter2, join as join4 } from "node:path";
|
|
35513
|
+
|
|
35514
|
+
// ../gogcli-mcp/src/attachment-root.ts
|
|
35515
|
+
import { tmpdir, userInfo } from "node:os";
|
|
35516
|
+
import { dirname as dirname2, join as join3, relative as relative2, resolve as resolve3, isAbsolute as isAbsolute3, sep as sep2 } from "node:path";
|
|
35517
|
+
var ATTACHMENT_DOWNLOAD_ROOT = join3(tmpdir(), `gogcli-mcp-attachments-${userInfo().uid}`);
|
|
35518
|
+
var ATTACHMENT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
35519
|
+
|
|
35520
|
+
// ../gogcli-mcp/src/file-roots.ts
|
|
35521
|
+
var FILE_ROOTS_ENV = "GOG_FILE_ROOTS";
|
|
35522
|
+
function defaultFileRoot() {
|
|
35523
|
+
return join4(homedir2(), "gogcli-mcp-files");
|
|
35524
|
+
}
|
|
35525
|
+
function fileRoots() {
|
|
35526
|
+
const raw = readEnvVar(FILE_ROOTS_ENV);
|
|
35527
|
+
const roots = (raw ?? "").split(delimiter2).map((r) => r.trim()).filter(Boolean);
|
|
35528
|
+
return roots.length > 0 ? roots : [defaultFileRoot()];
|
|
35529
|
+
}
|
|
35530
|
+
function confinePath(path, param, opts = {}) {
|
|
35531
|
+
if (opts.allowDash && path === "-") return path;
|
|
35532
|
+
const roots = [...fileRoots(), ATTACHMENT_DOWNLOAD_ROOT];
|
|
35533
|
+
try {
|
|
35534
|
+
assertPathWithinRoots(path, roots);
|
|
35535
|
+
} catch {
|
|
35536
|
+
throw new Error(
|
|
35537
|
+
`${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}.`
|
|
35538
|
+
);
|
|
35539
|
+
}
|
|
35540
|
+
return path;
|
|
35541
|
+
}
|
|
35542
|
+
|
|
35543
|
+
// ../gogcli-mcp/src/run-path-guard.ts
|
|
35544
|
+
var POSITIONAL_PATH_SUBCOMMANDS = {
|
|
35545
|
+
appscript: { pull: "gog_appscript_pull" },
|
|
35546
|
+
drive: { upload: "gog_drive_upload", sync: "gog_drive_sync_push" },
|
|
35547
|
+
gmail: { import: "gog_gmail_import" },
|
|
35548
|
+
slides: {
|
|
35549
|
+
"add-slide": "gog_slides_add_slide",
|
|
35550
|
+
"insert-image": "gog_slides_insert_image",
|
|
35551
|
+
"replace-slide": "gog_slides_replace_slide"
|
|
35552
|
+
}
|
|
35553
|
+
};
|
|
35554
|
+
var PATH_FLAGS = /* @__PURE__ */ new Set(["out", "output", "out-dir", "output-dir", "dir", "worker-dir", "attach", "file", "key", "cert", "replacements"]);
|
|
35555
|
+
var ID_FLAGS = {
|
|
35556
|
+
drive: /* @__PURE__ */ new Set(["file", "filter-file"])
|
|
35557
|
+
};
|
|
35558
|
+
var EXEC_FLAGS = /* @__PURE__ */ new Set(["on-change", "on-new", "mmdc"]);
|
|
35559
|
+
var SHORT_PATH_CLUSTER = /^-(?!-)[A-Za-z]*[of]/;
|
|
35560
|
+
var LONG_FLAG = /^--([A-Za-z0-9][A-Za-z0-9-]*)(?:=([\s\S]*))?$/;
|
|
35561
|
+
function isPathFlag(service, name) {
|
|
35562
|
+
if (ID_FLAGS[service]?.has(name)) return false;
|
|
35563
|
+
return PATH_FLAGS.has(name) || name.endsWith("-file");
|
|
35564
|
+
}
|
|
35565
|
+
function confineFlagValue(flag, value) {
|
|
35566
|
+
if (value === "-") return;
|
|
35567
|
+
confinePath(value, flag);
|
|
35568
|
+
for (const part of value.split(",")) {
|
|
35569
|
+
if (part !== "" && part !== "-") confinePath(part, flag);
|
|
35570
|
+
}
|
|
35571
|
+
}
|
|
35572
|
+
function positionalPathTool(service, subcommand) {
|
|
35573
|
+
return POSITIONAL_PATH_SUBCOMMANDS[service]?.[subcommand];
|
|
35574
|
+
}
|
|
35575
|
+
function assertRunPathsConfined(service, subcommand, args) {
|
|
35576
|
+
const dedicated = positionalPathTool(service, subcommand);
|
|
35577
|
+
if (dedicated) {
|
|
35578
|
+
throw new Error(
|
|
35579
|
+
`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.`
|
|
35580
|
+
);
|
|
35581
|
+
}
|
|
35582
|
+
for (let i = 0; i < args.length; i++) {
|
|
35583
|
+
const arg = args[i];
|
|
35584
|
+
if (SHORT_PATH_CLUSTER.test(arg)) {
|
|
35585
|
+
throw new Error(
|
|
35586
|
+
`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.`
|
|
35587
|
+
);
|
|
35588
|
+
}
|
|
35589
|
+
const match = LONG_FLAG.exec(arg);
|
|
35590
|
+
if (!match) continue;
|
|
35591
|
+
const name = match[1].toLowerCase();
|
|
35592
|
+
if (EXEC_FLAGS.has(name)) {
|
|
35593
|
+
throw new Error(`The flag --${name} is not allowed here: it runs a local program chosen by the caller.`);
|
|
35594
|
+
}
|
|
35595
|
+
const isJson = name.endsWith("-json");
|
|
35596
|
+
if (!isJson && !isPathFlag(service, name)) continue;
|
|
35597
|
+
let value = match[2];
|
|
35598
|
+
if (value === void 0) {
|
|
35599
|
+
value = args[i + 1];
|
|
35600
|
+
if (value === void 0) continue;
|
|
35601
|
+
i++;
|
|
35602
|
+
}
|
|
35603
|
+
if (isJson) {
|
|
35604
|
+
if (value.startsWith("@")) confineFlagValue(`--${name}`, value.slice(1));
|
|
35605
|
+
continue;
|
|
35606
|
+
}
|
|
35607
|
+
confineFlagValue(`--${name}`, value);
|
|
35608
|
+
}
|
|
35609
|
+
}
|
|
35610
|
+
|
|
35123
35611
|
// ../gogcli-mcp/src/tools/utils.ts
|
|
35124
35612
|
var accountParam = external_exports.string().optional().describe(
|
|
35125
35613
|
"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."
|
|
@@ -35164,9 +35652,17 @@ var paginationParams = {
|
|
|
35164
35652
|
all: external_exports.boolean().optional().describe("Fetch all pages")
|
|
35165
35653
|
};
|
|
35166
35654
|
function registerRunTool(server, options) {
|
|
35167
|
-
const { service, examples, omitAccount = false, note } = options;
|
|
35655
|
+
const { service, examples, omitAccount = false, note, allowedSubcommands, vet, gmailNoSend = false } = options;
|
|
35656
|
+
for (const [, example] of examples.matchAll(/"([^"]+)"/g)) {
|
|
35657
|
+
const refused = allowedSubcommands && !allowedSubcommands.includes(example) || vet?.(example, []) || positionalPathTool(service, example);
|
|
35658
|
+
if (refused) {
|
|
35659
|
+
throw new Error(`gog_${service}_run example "${example}" is refused by the tool itself; list a subcommand it runs.`);
|
|
35660
|
+
}
|
|
35661
|
+
}
|
|
35168
35662
|
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.`;
|
|
35169
|
-
const
|
|
35663
|
+
const restriction = allowedSubcommands ? ` Only these subcommands are available: ${allowedSubcommands.join(", ")}.` : "";
|
|
35664
|
+
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.';
|
|
35665
|
+
const description = `${baseDescription}${restriction}${safety}${note ? ` ${note}` : ""}`;
|
|
35170
35666
|
const inputSchema = {
|
|
35171
35667
|
subcommand: external_exports.string().describe(`The gog ${service} subcommand to run, e.g. ${examples}`),
|
|
35172
35668
|
args: external_exports.array(external_exports.string()).describe("Additional positional args and flags")
|
|
@@ -35180,7 +35676,21 @@ function registerRunTool(server, options) {
|
|
|
35180
35676
|
inputSchema: external_exports.object(inputSchema)
|
|
35181
35677
|
}, async (rawArgs) => {
|
|
35182
35678
|
const { subcommand, args, account } = rawArgs;
|
|
35183
|
-
|
|
35679
|
+
try {
|
|
35680
|
+
assertSafeSubcommand(subcommand);
|
|
35681
|
+
assertSafeForwardedArgs(args);
|
|
35682
|
+
if (allowedSubcommands && !allowedSubcommands.includes(subcommand)) {
|
|
35683
|
+
throw new Error(
|
|
35684
|
+
`gog ${service} ${subcommand} is not available through gog_${service}_run. Allowed: ${allowedSubcommands.join(", ")}.`
|
|
35685
|
+
);
|
|
35686
|
+
}
|
|
35687
|
+
const refusal = vet?.(subcommand, args);
|
|
35688
|
+
if (refusal) throw new Error(refusal);
|
|
35689
|
+
assertRunPathsConfined(service, subcommand, args);
|
|
35690
|
+
} catch (err) {
|
|
35691
|
+
return errorResult(errorText(err));
|
|
35692
|
+
}
|
|
35693
|
+
return runOrDiagnose([service, subcommand, ...args], gmailNoSend ? { account, gmailNoSend } : { account });
|
|
35184
35694
|
});
|
|
35185
35695
|
}
|
|
35186
35696
|
function errorText(err) {
|
|
@@ -35301,7 +35811,176 @@ function formatAuthHealth(raw, now) {
|
|
|
35301
35811
|
return accounts.map((a) => formatOneAccountHealth(a, now)).join("\n\n");
|
|
35302
35812
|
}
|
|
35303
35813
|
|
|
35814
|
+
// ../gogcli-mcp/src/send-confirm-token.ts
|
|
35815
|
+
import { createHash as createHash2, createHmac as createHmac2, randomBytes, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
35816
|
+
var CONFIRM_TOKEN_TTL_DEFAULT_SECONDS = 600;
|
|
35817
|
+
var PREFIX = "gct1";
|
|
35818
|
+
var secret;
|
|
35819
|
+
var spent = /* @__PURE__ */ new Map();
|
|
35820
|
+
function sendConfirmFallbackEnabled() {
|
|
35821
|
+
return readEnvVar("GOG_SEND_CONFIRM_FALLBACK")?.trim().toLowerCase() === "token";
|
|
35822
|
+
}
|
|
35823
|
+
function confirmTokenTtlSeconds() {
|
|
35824
|
+
const raw = readEnvVar("GOG_CONFIRM_TTL_SECONDS")?.trim();
|
|
35825
|
+
if (raw && /^\d+$/.test(raw)) {
|
|
35826
|
+
const n = Number(raw);
|
|
35827
|
+
if (n > 0) return n;
|
|
35828
|
+
}
|
|
35829
|
+
return CONFIRM_TOKEN_TTL_DEFAULT_SECONDS;
|
|
35830
|
+
}
|
|
35831
|
+
function hmacKey() {
|
|
35832
|
+
if (!secret) {
|
|
35833
|
+
const configured = readEnvVar("GOG_CONFIRM_SECRET");
|
|
35834
|
+
secret = configured ? Buffer.from(configured, "utf8") : randomBytes(32);
|
|
35835
|
+
}
|
|
35836
|
+
return secret;
|
|
35837
|
+
}
|
|
35838
|
+
function canonicalize(value) {
|
|
35839
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
35840
|
+
if (value && typeof value === "object") {
|
|
35841
|
+
const out = {};
|
|
35842
|
+
for (const key of Object.keys(value).sort()) {
|
|
35843
|
+
const v = value[key];
|
|
35844
|
+
if (v !== void 0) out[key] = canonicalize(v);
|
|
35845
|
+
}
|
|
35846
|
+
return out;
|
|
35847
|
+
}
|
|
35848
|
+
return value;
|
|
35849
|
+
}
|
|
35850
|
+
function hashSendPayload(payload) {
|
|
35851
|
+
return createHash2("sha256").update(JSON.stringify(canonicalize(payload))).digest("hex");
|
|
35852
|
+
}
|
|
35853
|
+
function sign(body) {
|
|
35854
|
+
return createHmac2("sha256", hmacKey()).update(`${PREFIX}.${body}`).digest("base64url");
|
|
35855
|
+
}
|
|
35856
|
+
function issueConfirmToken(binding, now = Date.now()) {
|
|
35857
|
+
const exp = now + confirmTokenTtlSeconds() * 1e3;
|
|
35858
|
+
const claims = {
|
|
35859
|
+
t: binding.tool,
|
|
35860
|
+
a: binding.account,
|
|
35861
|
+
g: binding.target,
|
|
35862
|
+
...binding.revision === void 0 ? {} : { r: binding.revision },
|
|
35863
|
+
h: binding.payloadHash,
|
|
35864
|
+
iat: now,
|
|
35865
|
+
exp,
|
|
35866
|
+
n: randomBytes(16).toString("base64url")
|
|
35867
|
+
};
|
|
35868
|
+
const body = Buffer.from(JSON.stringify(claims), "utf8").toString("base64url");
|
|
35869
|
+
return { token: `${PREFIX}.${body}.${sign(body)}`, expiresAt: new Date(exp).toISOString() };
|
|
35870
|
+
}
|
|
35871
|
+
function signatureMatches(body, sig) {
|
|
35872
|
+
const expected = Buffer.from(sign(body), "utf8");
|
|
35873
|
+
const actual = Buffer.from(sig, "utf8");
|
|
35874
|
+
return expected.length === actual.length && timingSafeEqual2(expected, actual);
|
|
35875
|
+
}
|
|
35876
|
+
function parseClaims(body) {
|
|
35877
|
+
try {
|
|
35878
|
+
return JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
|
|
35879
|
+
} catch {
|
|
35880
|
+
return void 0;
|
|
35881
|
+
}
|
|
35882
|
+
}
|
|
35883
|
+
function verifyConfirmToken(token, binding, now = Date.now()) {
|
|
35884
|
+
for (const [nonce, exp] of spent) if (exp < now) spent.delete(nonce);
|
|
35885
|
+
const parts = token.split(".");
|
|
35886
|
+
if (parts.length !== 3 || parts[0] !== PREFIX || !parts[1] || !parts[2]) return { ok: false, error: "TOKEN_INVALID" };
|
|
35887
|
+
const [, body, sig] = parts;
|
|
35888
|
+
if (!signatureMatches(body, sig)) return { ok: false, error: "TOKEN_INVALID" };
|
|
35889
|
+
const claims = parseClaims(body);
|
|
35890
|
+
if (!claims) return { ok: false, error: "TOKEN_INVALID" };
|
|
35891
|
+
if (claims.t !== binding.tool || claims.a !== binding.account || claims.g !== binding.target) {
|
|
35892
|
+
return { ok: false, error: "TOKEN_INVALID" };
|
|
35893
|
+
}
|
|
35894
|
+
if (spent.has(claims.n)) return { ok: false, error: "TOKEN_REUSED" };
|
|
35895
|
+
if (now > claims.exp) return { ok: false, error: "TOKEN_EXPIRED" };
|
|
35896
|
+
if (claims.r !== binding.revision) return { ok: false, error: "DRAFT_CHANGED", reason: "message-id-rotated" };
|
|
35897
|
+
if (claims.h !== binding.payloadHash) return { ok: false, error: "DRAFT_CHANGED", reason: "payload-changed" };
|
|
35898
|
+
spent.set(claims.n, claims.exp);
|
|
35899
|
+
return { ok: true };
|
|
35900
|
+
}
|
|
35901
|
+
|
|
35902
|
+
// ../gogcli-mcp/src/dispatch-confirmation.ts
|
|
35903
|
+
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.";
|
|
35904
|
+
var FALLBACK_HINT = "Or set GOG_SEND_CONFIRM_FALLBACK=token to enable two-step confirmation.";
|
|
35905
|
+
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.';
|
|
35906
|
+
var confirmTokenParam = external_exports.string().optional().describe(
|
|
35907
|
+
`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.`
|
|
35908
|
+
);
|
|
35909
|
+
var TOKEN_ERROR_NOTE = {
|
|
35910
|
+
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.",
|
|
35911
|
+
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.",
|
|
35912
|
+
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."
|
|
35913
|
+
};
|
|
35914
|
+
var DRAFT_CHANGED_NOTE = {
|
|
35915
|
+
"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.",
|
|
35916
|
+
"payload-changed": "Nothing was sent or changed: what would happen no longer matches what the user approved."
|
|
35917
|
+
};
|
|
35918
|
+
function isToolResult(value) {
|
|
35919
|
+
return Array.isArray(value.content);
|
|
35920
|
+
}
|
|
35921
|
+
function rejection2(data) {
|
|
35922
|
+
return { ...textResult({ status: "confirmation-rejected", confirmed: false, dispatched: false, ...data }), isError: true };
|
|
35923
|
+
}
|
|
35924
|
+
async function tokenConfirmation(op, fallback) {
|
|
35925
|
+
const subject = await fallback.subject();
|
|
35926
|
+
if (isToolResult(subject)) return subject;
|
|
35927
|
+
const binding = {
|
|
35928
|
+
tool: fallback.tool,
|
|
35929
|
+
account: fallback.account ?? readEnvVar("GOG_ACCOUNT") ?? "",
|
|
35930
|
+
target: subject.target,
|
|
35931
|
+
...subject.revision === void 0 ? {} : { revision: subject.revision },
|
|
35932
|
+
payloadHash: hashSendPayload(subject.payload)
|
|
35933
|
+
};
|
|
35934
|
+
const phaseOne = () => {
|
|
35935
|
+
const { token, expiresAt } = issueConfirmToken(binding);
|
|
35936
|
+
return {
|
|
35937
|
+
action: op,
|
|
35938
|
+
preview: subject.preview,
|
|
35939
|
+
confirmToken: token,
|
|
35940
|
+
expiresAt,
|
|
35941
|
+
ttlSeconds: confirmTokenTtlSeconds(),
|
|
35942
|
+
instruction: fallback.instruction ?? CONFIRM_ACTION_INSTRUCTION
|
|
35943
|
+
};
|
|
35944
|
+
};
|
|
35945
|
+
if (!fallback.confirmToken) {
|
|
35946
|
+
return textResult({ status: "confirmation-required", confirmed: false, dispatched: false, ...phaseOne() });
|
|
35947
|
+
}
|
|
35948
|
+
const verdict = verifyConfirmToken(fallback.confirmToken, binding);
|
|
35949
|
+
if (verdict.ok) return void 0;
|
|
35950
|
+
if (verdict.error === "DRAFT_CHANGED") {
|
|
35951
|
+
return rejection2({
|
|
35952
|
+
error: "DRAFT_CHANGED",
|
|
35953
|
+
reason: verdict.reason,
|
|
35954
|
+
note: `${DRAFT_CHANGED_NOTE[verdict.reason]} The current preview and a fresh confirmToken are below.`,
|
|
35955
|
+
...phaseOne()
|
|
35956
|
+
});
|
|
35957
|
+
}
|
|
35958
|
+
return rejection2({ error: verdict.error, action: op, note: TOKEN_ERROR_NOTE[verdict.error] });
|
|
35959
|
+
}
|
|
35960
|
+
function gatedElsewhere(what, via, does, tool) {
|
|
35961
|
+
return `${what} ${does} and is not available through ${via}. Use ${tool}, which asks the user to confirm.`;
|
|
35962
|
+
}
|
|
35963
|
+
async function requireDispatchConfirmation(ctx, options) {
|
|
35964
|
+
const { action, fallback } = options;
|
|
35965
|
+
if (fallback && callerAcceptsFormElicitation(ctx) === false && sendConfirmFallbackEnabled()) {
|
|
35966
|
+
return tokenConfirmation(action, fallback);
|
|
35967
|
+
}
|
|
35968
|
+
const note = fallback ? [options.unsupportedNote, FALLBACK_HINT].filter(Boolean).join(" ") : options.unsupportedNote;
|
|
35969
|
+
return requireConfirmation(ctx, {
|
|
35970
|
+
action,
|
|
35971
|
+
message: options.message,
|
|
35972
|
+
details: options.details,
|
|
35973
|
+
confirmationLabel: options.confirmationLabel,
|
|
35974
|
+
...note ? { unsupportedNote: note } : {}
|
|
35975
|
+
});
|
|
35976
|
+
}
|
|
35977
|
+
function resultText(result) {
|
|
35978
|
+
const first = result.content[0];
|
|
35979
|
+
return first && first.type === "text" && typeof first.text === "string" ? first.text : "{}";
|
|
35980
|
+
}
|
|
35981
|
+
|
|
35304
35982
|
// ../gogcli-mcp/src/tools/auth.ts
|
|
35983
|
+
var AUTH_RUN_SUBCOMMANDS = ["list", "status", "services", "remove", "alias"];
|
|
35305
35984
|
function registerAuthToolsWith(server, defaultServices) {
|
|
35306
35985
|
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.`;
|
|
35307
35986
|
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.";
|
|
@@ -35359,7 +36038,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
35359
36038
|
})
|
|
35360
36039
|
}, async ({ email: email3, services = defaultServices, extraScopes }) => {
|
|
35361
36040
|
try {
|
|
35362
|
-
const args = ["auth", "add", email3, "--services", services];
|
|
36041
|
+
const args = ["auth", "add", pos(email3), "--services", services];
|
|
35363
36042
|
if (extraScopes) args.push(`--extra-scopes=${extraScopes}`, "--force-consent");
|
|
35364
36043
|
return rawTextResult(await run(args, {
|
|
35365
36044
|
interactive: true,
|
|
@@ -35379,7 +36058,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
35379
36058
|
})
|
|
35380
36059
|
}, async ({ email: email3, services = defaultServices, extraScopes }) => {
|
|
35381
36060
|
try {
|
|
35382
|
-
const args = ["auth", "add", email3, "--remote", "--step", "1", "--services", services, "--force-consent"];
|
|
36061
|
+
const args = ["auth", "add", pos(email3), "--remote", "--step", "1", "--services", services, "--force-consent"];
|
|
35383
36062
|
if (extraScopes) args.push(`--extra-scopes=${extraScopes}`);
|
|
35384
36063
|
return rawTextResult(await run(args, { redactMode: "tokens" }));
|
|
35385
36064
|
} catch (err) {
|
|
@@ -35406,7 +36085,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
35406
36085
|
const args = [
|
|
35407
36086
|
"auth",
|
|
35408
36087
|
"add",
|
|
35409
|
-
email3,
|
|
36088
|
+
pos(email3),
|
|
35410
36089
|
"--remote",
|
|
35411
36090
|
"--step",
|
|
35412
36091
|
"2",
|
|
@@ -35426,6 +36105,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
35426
36105
|
service: "auth",
|
|
35427
36106
|
examples: '"remove", "alias", "list"',
|
|
35428
36107
|
omitAccount: true,
|
|
36108
|
+
allowedSubcommands: AUTH_RUN_SUBCOMMANDS,
|
|
35429
36109
|
note: "For browser-based authorization, use gog_auth_add instead."
|
|
35430
36110
|
});
|
|
35431
36111
|
}
|
|
@@ -35434,6 +36114,54 @@ function authToolsFor(defaultServices) {
|
|
|
35434
36114
|
}
|
|
35435
36115
|
|
|
35436
36116
|
// ../gogcli-mcp/src/tools/calendar.ts
|
|
36117
|
+
var CALENDAR_GATED = {
|
|
36118
|
+
create: "gog_calendar_create",
|
|
36119
|
+
add: "gog_calendar_create",
|
|
36120
|
+
new: "gog_calendar_create",
|
|
36121
|
+
update: "gog_calendar_update",
|
|
36122
|
+
edit: "gog_calendar_update",
|
|
36123
|
+
set: "gog_calendar_update",
|
|
36124
|
+
respond: "gog_calendar_respond",
|
|
36125
|
+
rsvp: "gog_calendar_respond",
|
|
36126
|
+
reply: "gog_calendar_respond"
|
|
36127
|
+
};
|
|
36128
|
+
function vetCalendarRun(subcommand, _args) {
|
|
36129
|
+
const sub = subcommand.toLowerCase();
|
|
36130
|
+
const tool = Object.hasOwn(CALENDAR_GATED, sub) ? CALENDAR_GATED[sub] : void 0;
|
|
36131
|
+
return tool ? gatedElsewhere(`gog calendar ${sub}`, "gog_calendar_run", "can change what guests see", tool) : void 0;
|
|
36132
|
+
}
|
|
36133
|
+
function parseAttendees(value) {
|
|
36134
|
+
if (!value) return [];
|
|
36135
|
+
return value.split(",").map((entry) => entry.split(";")).filter((parts) => !parts.slice(1).some((m) => m.trim().toLowerCase() === "resource")).map((parts) => parts[0].trim()).filter(Boolean);
|
|
36136
|
+
}
|
|
36137
|
+
function timeOf(t) {
|
|
36138
|
+
const v = t?.dateTime ?? t?.date;
|
|
36139
|
+
return typeof v === "string" ? v : void 0;
|
|
36140
|
+
}
|
|
36141
|
+
function eventSnapshot(raw) {
|
|
36142
|
+
let event;
|
|
36143
|
+
try {
|
|
36144
|
+
event = JSON.parse(raw)?.event;
|
|
36145
|
+
} catch {
|
|
36146
|
+
event = void 0;
|
|
36147
|
+
}
|
|
36148
|
+
const str = (v) => typeof v === "string" ? v : void 0;
|
|
36149
|
+
return {
|
|
36150
|
+
summary: str(event?.summary),
|
|
36151
|
+
start: timeOf(event?.start),
|
|
36152
|
+
end: timeOf(event?.end),
|
|
36153
|
+
organizer: str(event?.organizer?.email),
|
|
36154
|
+
// Guests are the OTHER people: not this account's own attendee row, not rooms.
|
|
36155
|
+
guests: (Array.isArray(event?.attendees) ? event.attendees : []).filter((a) => a.self !== true && a.resource !== true && typeof a.email === "string").map((a) => a.email),
|
|
36156
|
+
etag: str(event?.etag)
|
|
36157
|
+
};
|
|
36158
|
+
}
|
|
36159
|
+
async function readEvent(calendarId, eventId, account) {
|
|
36160
|
+
const got = await runOrDiagnose(["calendar", "event", pos(calendarId), pos(eventId)], { account });
|
|
36161
|
+
if (got.isError) return { error: got };
|
|
36162
|
+
return { event: eventSnapshot(resultText(got)) };
|
|
36163
|
+
}
|
|
36164
|
+
var CALENDAR_CONFIRM_LABEL = "Confirm that this change, which other people will see, should be made now.";
|
|
35437
36165
|
var reminderParams = {
|
|
35438
36166
|
reminders: external_exports.array(external_exports.string()).max(5).optional().describe(
|
|
35439
36167
|
`Reminders as method:duration, e.g. ["popup:30m", "email:1d"]. Method is popup or email; duration accepts m/h/d (max 40320 minutes = 4 weeks). Google allows at most 5. These REPLACE the event's reminders \u2014 on update, pass an EMPTY array to drop custom reminders and go back to the calendar's defaults. Cannot be combined with noReminders.`
|
|
@@ -35487,7 +36215,7 @@ function registerCalendarTools(server) {
|
|
|
35487
36215
|
})
|
|
35488
36216
|
}, async ({ calendarId, from, to, days, today, query, max, pageToken, page, all, eventTypes, timezone, view, account }) => {
|
|
35489
36217
|
const args = ["calendar", "events"];
|
|
35490
|
-
if (calendarId) args.push(calendarId);
|
|
36218
|
+
if (calendarId) args.push(pos(calendarId));
|
|
35491
36219
|
if (from) args.push(`--from=${from}`);
|
|
35492
36220
|
if (to) args.push(`--to=${to}`);
|
|
35493
36221
|
if (days !== void 0) args.push(`--days=${days}`);
|
|
@@ -35516,12 +36244,12 @@ function registerCalendarTools(server) {
|
|
|
35516
36244
|
account: accountParam
|
|
35517
36245
|
})
|
|
35518
36246
|
}, async ({ calendarId, eventId, timezone, account }) => {
|
|
35519
|
-
const args = ["calendar", "event", calendarId, eventId];
|
|
36247
|
+
const args = ["calendar", "event", pos(calendarId), pos(eventId)];
|
|
35520
36248
|
if (timezone) args.push(`--timezone=${timezone}`);
|
|
35521
36249
|
return runOrDiagnose(args, { account });
|
|
35522
36250
|
});
|
|
35523
36251
|
server.registerTool("gog_calendar_create", {
|
|
35524
|
-
description: "Create a calendar event. Set withZoom=true to attach a Zoom meeting (requires Zoom S2S OAuth setup via gog_zoom_auth_setup; the join URL + meeting ID + passcode are appended to the event description \u2014 Google rejects native conference card writes from non-Workspace-Marketplace OAuth clients).",
|
|
36252
|
+
description: "Create a calendar event. Set withZoom=true to attach a Zoom meeting (requires Zoom S2S OAuth setup via gog_zoom_auth_setup; the join URL + meeting ID + passcode are appended to the event description \u2014 Google rejects native conference card writes from non-Workspace-Marketplace OAuth clients). With attendees, the event lands on other people's calendars (no invitation email is sent), so the MCP host is asked to show the user a confirmation prompt with the guests, time and details first; a guest-free event is created without asking." + CONFIRM_FALLBACK_DESCRIPTION,
|
|
35525
36253
|
annotations: { destructiveHint: false },
|
|
35526
36254
|
inputSchema: external_exports.object({
|
|
35527
36255
|
calendarId: external_exports.string().describe('Calendar ID (use "primary" for the default calendar)'),
|
|
@@ -35535,10 +36263,11 @@ function registerCalendarTools(server) {
|
|
|
35535
36263
|
timezone: external_exports.string().optional().describe("IANA timezone metadata applied to from/to (e.g. America/New_York). Sets both start and end timezone unless start/end timezone are overridden."),
|
|
35536
36264
|
withZoom: external_exports.boolean().optional().describe("Create a Zoom video conference for this event (requires Zoom S2S OAuth setup)"),
|
|
35537
36265
|
...reminderParams,
|
|
35538
|
-
account: accountParam
|
|
36266
|
+
account: accountParam,
|
|
36267
|
+
confirmToken: confirmTokenParam
|
|
35539
36268
|
})
|
|
35540
|
-
}, async ({ calendarId, summary, from, to, description, location, attendees, allDay, timezone, withZoom, reminders, noReminders, account }) => {
|
|
35541
|
-
const args = ["calendar", "create", calendarId, `--summary=${summary}`, `--from=${from}`, `--to=${to}`];
|
|
36269
|
+
}, async ({ calendarId, summary, from, to, description, location, attendees, allDay, timezone, withZoom, reminders, noReminders, account, confirmToken }, ctx) => {
|
|
36270
|
+
const args = ["calendar", "create", pos(calendarId), `--summary=${summary}`, `--from=${from}`, `--to=${to}`];
|
|
35542
36271
|
if (description) args.push(`--description=${description}`);
|
|
35543
36272
|
if (location) args.push(`--location=${location}`);
|
|
35544
36273
|
if (attendees) args.push(`--attendees=${attendees}`);
|
|
@@ -35546,10 +36275,28 @@ function registerCalendarTools(server) {
|
|
|
35546
36275
|
if (timezone) args.push(`--timezone=${timezone}`);
|
|
35547
36276
|
if (withZoom) args.push("--with-zoom");
|
|
35548
36277
|
pushReminderFlags(args, { reminders, noReminders });
|
|
36278
|
+
const guests = parseAttendees(attendees);
|
|
36279
|
+
if (guests.length > 0) {
|
|
36280
|
+
const event = { calendarId, summary, from, to, allDay: Boolean(allDay), timezone, location, description, guests, attendees, withZoom: Boolean(withZoom) };
|
|
36281
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
36282
|
+
action: "calendar.create",
|
|
36283
|
+
message: "Review and confirm this event, which will appear on the guests' calendars:",
|
|
36284
|
+
confirmationLabel: CALENDAR_CONFIRM_LABEL,
|
|
36285
|
+
details: event,
|
|
36286
|
+
unsupportedNote: "Create it without attendees instead; the user can add the guests in Google Calendar.",
|
|
36287
|
+
fallback: {
|
|
36288
|
+
tool: "gog_calendar_create",
|
|
36289
|
+
account,
|
|
36290
|
+
confirmToken,
|
|
36291
|
+
subject: () => ({ target: calendarId, payload: event, preview: event })
|
|
36292
|
+
}
|
|
36293
|
+
});
|
|
36294
|
+
if (confirmation) return confirmation;
|
|
36295
|
+
}
|
|
35549
36296
|
return runOrDiagnose(args, { account });
|
|
35550
36297
|
});
|
|
35551
36298
|
server.registerTool("gog_calendar_update", {
|
|
35552
|
-
description: "Update an existing calendar event. Zoom: withZoom adds a Zoom meeting, regenerateZoom replaces the existing one, removeZoom strips it. removeMeet clears the event's Google Meet conference data (e.g. before attaching another provider). Conference flags are independent \u2014 use one per call.",
|
|
36299
|
+
description: "Update an existing calendar event. Zoom: withZoom adds a Zoom meeting, regenerateZoom replaces the existing one, removeZoom strips it. removeMeet clears the event's Google Meet conference data (e.g. before attaching another provider). Conference flags are independent \u2014 use one per call. A change guests can see (time, title, place, description, attendees, attachments, conferencing) on an event that has guests \u2014 or gains them \u2014 reads the event and asks the MCP host to show the user a confirmation prompt with the event as it stands and the change; no invitation email is sent. Reminder-only changes and guest-free events are updated without asking." + CONFIRM_FALLBACK_DESCRIPTION,
|
|
35553
36300
|
annotations: { destructiveHint: false },
|
|
35554
36301
|
inputSchema: external_exports.object({
|
|
35555
36302
|
calendarId: external_exports.string().describe("Calendar ID"),
|
|
@@ -35567,10 +36314,11 @@ function registerCalendarTools(server) {
|
|
|
35567
36314
|
removeZoom: external_exports.boolean().optional().describe("Remove the event's Zoom video conference"),
|
|
35568
36315
|
removeMeet: external_exports.boolean().optional().describe("Remove the event's Google Meet video conference (clears conference data only)"),
|
|
35569
36316
|
...reminderParams,
|
|
35570
|
-
account: accountParam
|
|
36317
|
+
account: accountParam,
|
|
36318
|
+
confirmToken: confirmTokenParam
|
|
35571
36319
|
})
|
|
35572
|
-
}, async ({ calendarId, eventId, summary, from, to, description, location, attendees, addAttendees, attachments, withZoom, regenerateZoom, removeZoom, removeMeet, reminders, noReminders, account }) => {
|
|
35573
|
-
const args = ["calendar", "update", calendarId, eventId];
|
|
36320
|
+
}, async ({ calendarId, eventId, summary, from, to, description, location, attendees, addAttendees, attachments, withZoom, regenerateZoom, removeZoom, removeMeet, reminders, noReminders, account, confirmToken }, ctx) => {
|
|
36321
|
+
const args = ["calendar", "update", pos(calendarId), pos(eventId)];
|
|
35574
36322
|
if (summary !== void 0) args.push(`--summary=${summary}`);
|
|
35575
36323
|
if (from !== void 0) args.push(`--from=${from}`);
|
|
35576
36324
|
if (to !== void 0) args.push(`--to=${to}`);
|
|
@@ -35584,6 +36332,45 @@ function registerCalendarTools(server) {
|
|
|
35584
36332
|
if (removeZoom) args.push("--remove-zoom");
|
|
35585
36333
|
if (removeMeet) args.push("--remove-meet");
|
|
35586
36334
|
pushReminderFlags(args, { reminders, noReminders });
|
|
36335
|
+
const changes = Object.fromEntries(Object.entries({
|
|
36336
|
+
summary,
|
|
36337
|
+
from,
|
|
36338
|
+
to,
|
|
36339
|
+
description,
|
|
36340
|
+
location,
|
|
36341
|
+
attendees,
|
|
36342
|
+
addAttendees,
|
|
36343
|
+
attachments,
|
|
36344
|
+
withZoom,
|
|
36345
|
+
regenerateZoom,
|
|
36346
|
+
removeZoom,
|
|
36347
|
+
removeMeet
|
|
36348
|
+
}).filter(([, v]) => v !== void 0 && v !== false));
|
|
36349
|
+
if (Object.keys(changes).length > 0) {
|
|
36350
|
+
const read = await readEvent(calendarId, eventId, account);
|
|
36351
|
+
if (read.error) return read.error;
|
|
36352
|
+
const { etag, ...current } = read.event;
|
|
36353
|
+
const added = [...parseAttendees(attendees), ...parseAttendees(addAttendees)];
|
|
36354
|
+
if (current.guests.length > 0 || added.length > 0) {
|
|
36355
|
+
const view = { calendarId, eventId, current, changes };
|
|
36356
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
36357
|
+
action: "calendar.update",
|
|
36358
|
+
message: "Review and confirm this change to an event other people are on:",
|
|
36359
|
+
confirmationLabel: CALENDAR_CONFIRM_LABEL,
|
|
36360
|
+
details: view,
|
|
36361
|
+
unsupportedNote: "Ask the user to make this change in Google Calendar.",
|
|
36362
|
+
fallback: {
|
|
36363
|
+
tool: "gog_calendar_update",
|
|
36364
|
+
account,
|
|
36365
|
+
confirmToken,
|
|
36366
|
+
// The etag rotates on any edit, so an event changed elsewhere between
|
|
36367
|
+
// the two phases is DRAFT_CHANGED rather than edited over.
|
|
36368
|
+
subject: () => ({ target: `${calendarId}/${eventId}`, revision: etag, payload: view, preview: view })
|
|
36369
|
+
}
|
|
36370
|
+
});
|
|
36371
|
+
if (confirmation) return confirmation;
|
|
36372
|
+
}
|
|
36373
|
+
}
|
|
35587
36374
|
return runOrDiagnose(args, { account });
|
|
35588
36375
|
});
|
|
35589
36376
|
server.registerTool("gog_calendar_delete", {
|
|
@@ -35595,24 +36382,43 @@ function registerCalendarTools(server) {
|
|
|
35595
36382
|
account: accountParam
|
|
35596
36383
|
})
|
|
35597
36384
|
}, async ({ calendarId, eventId, account }) => {
|
|
35598
|
-
return runOrDiagnose(["calendar", "delete", calendarId, eventId, "--force"], { account });
|
|
36385
|
+
return runOrDiagnose(["calendar", "delete", pos(calendarId), pos(eventId), "--force"], { account });
|
|
35599
36386
|
});
|
|
35600
36387
|
server.registerTool("gog_calendar_respond", {
|
|
35601
|
-
description: "Respond to a calendar event invitation.",
|
|
36388
|
+
description: "Respond to a calendar event invitation. The organizer sees the response, so this reads the event and asks the MCP host to show the user a confirmation prompt with the event, organizer, response and comment; nothing is recorded unless they accept." + CONFIRM_FALLBACK_DESCRIPTION,
|
|
35602
36389
|
annotations: { destructiveHint: true },
|
|
35603
36390
|
inputSchema: external_exports.object({
|
|
35604
36391
|
calendarId: external_exports.string().describe("Calendar ID"),
|
|
35605
36392
|
eventId: external_exports.string().describe("Event ID"),
|
|
35606
36393
|
status: external_exports.enum(["accepted", "declined", "tentative"]).describe("Response status"),
|
|
35607
36394
|
comment: external_exports.string().optional().describe("Optional comment to include with response"),
|
|
35608
|
-
account: accountParam
|
|
36395
|
+
account: accountParam,
|
|
36396
|
+
confirmToken: confirmTokenParam
|
|
35609
36397
|
})
|
|
35610
|
-
}, async ({ calendarId, eventId, status, comment, account }) => {
|
|
35611
|
-
const args = ["calendar", "respond", calendarId, eventId, `--status=${status}`];
|
|
36398
|
+
}, async ({ calendarId, eventId, status, comment, account, confirmToken }, ctx) => {
|
|
36399
|
+
const args = ["calendar", "respond", pos(calendarId), pos(eventId), `--status=${status}`];
|
|
35612
36400
|
if (comment) args.push(`--comment=${comment}`);
|
|
36401
|
+
const read = await readEvent(calendarId, eventId, account);
|
|
36402
|
+
if (read.error) return read.error;
|
|
36403
|
+
const { etag, ...event } = read.event;
|
|
36404
|
+
const view = { calendarId, eventId, event, response: status, ...comment ? { comment } : {} };
|
|
36405
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
36406
|
+
action: "calendar.respond",
|
|
36407
|
+
message: "Review and confirm this response, which the organizer will see:",
|
|
36408
|
+
confirmationLabel: "Confirm that this response should be recorded now.",
|
|
36409
|
+
details: view,
|
|
36410
|
+
unsupportedNote: "Ask the user to respond from Google Calendar.",
|
|
36411
|
+
fallback: {
|
|
36412
|
+
tool: "gog_calendar_respond",
|
|
36413
|
+
account,
|
|
36414
|
+
confirmToken,
|
|
36415
|
+
subject: () => ({ target: `${calendarId}/${eventId}`, revision: etag, payload: view, preview: view })
|
|
36416
|
+
}
|
|
36417
|
+
});
|
|
36418
|
+
if (confirmation) return confirmation;
|
|
35613
36419
|
return runOrDiagnose(args, { account });
|
|
35614
36420
|
});
|
|
35615
|
-
registerRunTool(server, { service: "calendar", examples: '"calendars", "freebusy"' });
|
|
36421
|
+
registerRunTool(server, { service: "calendar", examples: '"calendars", "freebusy"', vet: vetCalendarRun });
|
|
35616
36422
|
}
|
|
35617
36423
|
|
|
35618
36424
|
// ../gogcli-mcp/src/attachments.ts
|
|
@@ -35647,7 +36453,7 @@ var replySchema = {
|
|
|
35647
36453
|
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."),
|
|
35648
36454
|
subject: external_exports.string().optional().describe('Override reply subject (default: "Re: <original>"). A changed subject starts a NEW Gmail thread.'),
|
|
35649
36455
|
noQuote: external_exports.boolean().optional().describe("Do not include the original message quoted below the reply (default: the original is quoted)"),
|
|
35650
|
-
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.`),
|
|
36456
|
+
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).`),
|
|
35651
36457
|
attachInline: attachInlineParam,
|
|
35652
36458
|
from: external_exports.string().optional().describe("Send from this email address (must be a verified send-as alias)"),
|
|
35653
36459
|
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."),
|
|
@@ -35656,7 +36462,7 @@ var replySchema = {
|
|
|
35656
36462
|
signatureFile: external_exports.string().optional().describe("Append a local signature file (plain text or HTML), read on the gog server"),
|
|
35657
36463
|
account: accountParam
|
|
35658
36464
|
};
|
|
35659
|
-
var sendReplySchema = external_exports.object(replySchema);
|
|
36465
|
+
var sendReplySchema = external_exports.object({ ...replySchema, confirmToken: confirmTokenParam });
|
|
35660
36466
|
|
|
35661
36467
|
// ../gogcli-mcp/src/tools/sheets.ts
|
|
35662
36468
|
var cellValueParam = external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean(), external_exports.null()]);
|
|
@@ -35668,10 +36474,10 @@ var failIfNotEmptyParam = external_exports.boolean().optional().describe(
|
|
|
35668
36474
|
);
|
|
35669
36475
|
|
|
35670
36476
|
// ../gogcli-mcp/src/server.ts
|
|
35671
|
-
var VERSION = true ? "4.
|
|
36477
|
+
var VERSION = true ? "4.4.0" : "0.0.0";
|
|
35672
36478
|
|
|
35673
36479
|
// ../gogcli-mcp/src/bootstrap-auth.ts
|
|
35674
|
-
import { join as
|
|
36480
|
+
import { join as join5 } from "node:path";
|
|
35675
36481
|
var AUTH_BOOTSTRAP_MARKER = "auth-bootstrap.sha256";
|
|
35676
36482
|
var VARS = ["GOG_CLIENT_ID", "GOG_CLIENT_SECRET", "GOG_REFRESH_TOKEN", "GOG_ACCOUNT"];
|
|
35677
36483
|
var log = (line) => {
|
|
@@ -35705,12 +36511,12 @@ async function bootstrapGogAuth(env = process.env, options = {}) {
|
|
|
35705
36511
|
const [clientId, clientSecret, refreshToken, account] = values;
|
|
35706
36512
|
const { spawner } = options;
|
|
35707
36513
|
try {
|
|
35708
|
-
const { createHash } = await import("node:crypto");
|
|
36514
|
+
const { createHash: createHash3 } = await import("node:crypto");
|
|
35709
36515
|
const { mkdir, readFile, writeFile, chmod } = await import("node:fs/promises");
|
|
35710
36516
|
const home = options.home ?? (await import("node:os")).homedir();
|
|
35711
|
-
const dir =
|
|
35712
|
-
const marker =
|
|
35713
|
-
const fingerprint =
|
|
36517
|
+
const dir = join5(home, ".gogcli-mcp");
|
|
36518
|
+
const marker = join5(dir, AUTH_BOOTSTRAP_MARKER);
|
|
36519
|
+
const fingerprint = createHash3("sha256").update(values.join("\0")).digest("hex");
|
|
35714
36520
|
const previous = await readFile(marker, "utf8").catch(() => void 0);
|
|
35715
36521
|
if (previous === fingerprint && await accountListed(account, spawner)) return "present";
|
|
35716
36522
|
await run(
|
|
@@ -35767,7 +36573,7 @@ function registerExtraCalendarTools(server) {
|
|
|
35767
36573
|
account: accountParam
|
|
35768
36574
|
})
|
|
35769
36575
|
}, async ({ meetingCode, account }) => {
|
|
35770
|
-
return runOrDiagnose(["meet", "get", meetingCode], { account });
|
|
36576
|
+
return runOrDiagnose(["meet", "get", pos(meetingCode)], { account });
|
|
35771
36577
|
});
|
|
35772
36578
|
server.registerTool("gog_meet_update", {
|
|
35773
36579
|
description: "Update a Google Meet space configuration.",
|
|
@@ -35778,7 +36584,7 @@ function registerExtraCalendarTools(server) {
|
|
|
35778
36584
|
account: accountParam
|
|
35779
36585
|
})
|
|
35780
36586
|
}, async ({ meetingCode, access, account }) => {
|
|
35781
|
-
const args = ["meet", "update", meetingCode];
|
|
36587
|
+
const args = ["meet", "update", pos(meetingCode)];
|
|
35782
36588
|
if (access) args.push(`--access=${access}`);
|
|
35783
36589
|
return runOrDiagnose(args, { account });
|
|
35784
36590
|
});
|
|
@@ -35790,7 +36596,7 @@ function registerExtraCalendarTools(server) {
|
|
|
35790
36596
|
account: accountParam
|
|
35791
36597
|
})
|
|
35792
36598
|
}, async ({ meetingCode, account }) => {
|
|
35793
|
-
return runOrDiagnose(["meet", "end", meetingCode, "--force"], { account });
|
|
36599
|
+
return runOrDiagnose(["meet", "end", pos(meetingCode), "--force"], { account });
|
|
35794
36600
|
});
|
|
35795
36601
|
server.registerTool("gog_meet_history", {
|
|
35796
36602
|
description: "List past calls (conferences) in a Google Meet space.",
|
|
@@ -35804,7 +36610,7 @@ function registerExtraCalendarTools(server) {
|
|
|
35804
36610
|
account: accountParam
|
|
35805
36611
|
})
|
|
35806
36612
|
}, async ({ meetingCode, max, pageToken, page, all, account }) => {
|
|
35807
|
-
const args = ["meet", "history", meetingCode];
|
|
36613
|
+
const args = ["meet", "history", pos(meetingCode)];
|
|
35808
36614
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
35809
36615
|
const token = resolvePageToken({ pageToken, page });
|
|
35810
36616
|
if (token) args.push(`--page=${token}`);
|
|
@@ -35879,7 +36685,7 @@ function registerExtraCalendarTools(server) {
|
|
|
35879
36685
|
account: accountParam
|
|
35880
36686
|
})
|
|
35881
36687
|
}, async ({ query, from, to, today, tomorrow, week, days, weekStart, calendar, max, account }) => {
|
|
35882
|
-
const args = ["calendar", "search", query];
|
|
36688
|
+
const args = ["calendar", "search", pos(query)];
|
|
35883
36689
|
if (from) args.push(`--from=${from}`);
|
|
35884
36690
|
if (to) args.push(`--to=${to}`);
|
|
35885
36691
|
if (today) args.push("--today");
|
|
@@ -35904,7 +36710,7 @@ function registerExtraCalendarTools(server) {
|
|
|
35904
36710
|
})
|
|
35905
36711
|
}, async ({ calendarId, calendarIds, since, max, all, account }) => {
|
|
35906
36712
|
const args = ["calendar", "changed"];
|
|
35907
|
-
if (calendarId) args.push(calendarId);
|
|
36713
|
+
if (calendarId) args.push(pos(calendarId));
|
|
35908
36714
|
if (calendarIds) args.push(`--calendars=${calendarIds}`);
|
|
35909
36715
|
if (since) args.push(`--since=${since}`);
|
|
35910
36716
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
@@ -35923,7 +36729,7 @@ function registerExtraCalendarTools(server) {
|
|
|
35923
36729
|
})
|
|
35924
36730
|
}, async ({ from, to, calendarIds, all, account }) => {
|
|
35925
36731
|
const args = ["calendar", "freebusy"];
|
|
35926
|
-
if (calendarIds) args.push(calendarIds);
|
|
36732
|
+
if (calendarIds) args.push(pos(calendarIds));
|
|
35927
36733
|
args.push(`--from=${from}`);
|
|
35928
36734
|
args.push(`--to=${to}`);
|
|
35929
36735
|
if (all) args.push("--all");
|
|
@@ -35950,7 +36756,7 @@ function registerExtraCalendarTools(server) {
|
|
|
35950
36756
|
account: accountParam
|
|
35951
36757
|
})
|
|
35952
36758
|
}, async ({ calendarId, max, pageToken, page, all, account }) => {
|
|
35953
|
-
const args = ["calendar", "acl", calendarId];
|
|
36759
|
+
const args = ["calendar", "acl", pos(calendarId)];
|
|
35954
36760
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
35955
36761
|
const token = resolvePageToken({ pageToken, page });
|
|
35956
36762
|
if (token) args.push(`--page=${token}`);
|
|
@@ -35968,7 +36774,7 @@ function registerExtraCalendarTools(server) {
|
|
|
35968
36774
|
account: accountParam
|
|
35969
36775
|
})
|
|
35970
36776
|
}, async ({ calendarId, eventId, destinationCalendarId, sendUpdates, account }) => {
|
|
35971
|
-
const args = ["calendar", "move", calendarId, eventId, destinationCalendarId];
|
|
36777
|
+
const args = ["calendar", "move", pos(calendarId), pos(eventId), pos(destinationCalendarId)];
|
|
35972
36778
|
if (sendUpdates) args.push(`--send-updates=${sendUpdates}`);
|
|
35973
36779
|
return runOrDiagnose(args, { account });
|
|
35974
36780
|
});
|
|
@@ -35987,7 +36793,7 @@ function registerExtraCalendarTools(server) {
|
|
|
35987
36793
|
})
|
|
35988
36794
|
}, async ({ from, to, calendarId, summary, autoDecline, declineMessage, allDay, account }) => {
|
|
35989
36795
|
const args = ["calendar", "out-of-office"];
|
|
35990
|
-
if (calendarId) args.push(calendarId);
|
|
36796
|
+
if (calendarId) args.push(pos(calendarId));
|
|
35991
36797
|
args.push(`--from=${from}`);
|
|
35992
36798
|
args.push(`--to=${to}`);
|
|
35993
36799
|
if (summary) args.push(`--summary=${summary}`);
|
|
@@ -36004,7 +36810,7 @@ function registerExtraCalendarTools(server) {
|
|
|
36004
36810
|
account: accountParam
|
|
36005
36811
|
})
|
|
36006
36812
|
}, async ({ calendarId, account }) => {
|
|
36007
|
-
return runOrDiagnose(["calendar", "unsubscribe", calendarId], { account });
|
|
36813
|
+
return runOrDiagnose(["calendar", "unsubscribe", pos(calendarId)], { account });
|
|
36008
36814
|
});
|
|
36009
36815
|
server.registerTool("gog_calendar_delete_calendar", {
|
|
36010
36816
|
description: "Permanently delete an owned secondary calendar and all its events. Cannot delete your primary calendar. To merely remove a calendar you do not own from your list, use gog_calendar_unsubscribe.",
|
|
@@ -36014,7 +36820,7 @@ function registerExtraCalendarTools(server) {
|
|
|
36014
36820
|
account: accountParam
|
|
36015
36821
|
})
|
|
36016
36822
|
}, async ({ calendarId, account }) => {
|
|
36017
|
-
return runOrDiagnose(["calendar", "delete-calendar", calendarId, "--force"], { account });
|
|
36823
|
+
return runOrDiagnose(["calendar", "delete-calendar", pos(calendarId), "--force"], { account });
|
|
36018
36824
|
});
|
|
36019
36825
|
server.registerTool("gog_meet_participants", {
|
|
36020
36826
|
description: "List participants from the latest (or a specific) Meet call.",
|
|
@@ -36029,7 +36835,7 @@ function registerExtraCalendarTools(server) {
|
|
|
36029
36835
|
account: accountParam
|
|
36030
36836
|
})
|
|
36031
36837
|
}, async ({ meetingCode, conference, max, pageToken, page, all, account }) => {
|
|
36032
|
-
const args = ["meet", "participants", meetingCode];
|
|
36838
|
+
const args = ["meet", "participants", pos(meetingCode)];
|
|
36033
36839
|
if (conference) args.push(`--conference=${conference}`);
|
|
36034
36840
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
36035
36841
|
const token = resolvePageToken({ pageToken, page });
|