gogcli-mcp 4.2.5 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/index.js +714 -216
- package/dist/lib.js +750 -216
- package/manifest.json +9 -2
- package/package.json +2 -2
- package/server.json +2 -2
- package/src/arg-guard.ts +68 -0
- package/src/argv.ts +27 -0
- package/src/attachment-root.ts +111 -0
- package/src/attachments.ts +1 -0
- package/src/blob-upload.ts +4 -2
- package/src/file-roots.ts +66 -0
- package/src/gmail-dispatch-guard.ts +56 -16
- package/src/gmail-results.ts +21 -2
- package/src/lib.ts +6 -0
- package/src/run-path-guard.ts +118 -0
- package/src/runner.ts +86 -18
- package/src/tools/api.ts +39 -7
- package/src/tools/appscript.ts +12 -8
- package/src/tools/auth.ts +13 -3
- package/src/tools/calendar.ts +10 -8
- package/src/tools/chat.ts +16 -13
- package/src/tools/classroom.ts +27 -25
- package/src/tools/contacts.ts +5 -3
- package/src/tools/docs.ts +8 -6
- package/src/tools/drive.ts +42 -18
- package/src/tools/gmail.ts +71 -11
- package/src/tools/sheets.ts +10 -8
- package/src/tools/slides.ts +14 -9
- package/src/tools/tasks.ts +7 -5
- package/src/tools/utils.ts +52 -6
- package/tests/arg-guard.test.ts +80 -0
- package/tests/attachment-root.test.ts +130 -0
- package/tests/attachments.test.ts +8 -0
- package/tests/blob-upload.test.ts +4 -3
- package/tests/file-roots.test.ts +101 -0
- package/tests/gmail-dispatch-guard.test.ts +46 -0
- package/tests/gmail-results.test.ts +35 -1
- package/tests/run-path-guard.test.ts +142 -0
- package/tests/runner.test.ts +136 -0
- package/tests/tools/api.test.ts +74 -8
- package/tests/tools/appscript.test.ts +34 -8
- package/tests/tools/auth.test.ts +44 -17
- package/tests/tools/calendar.test.ts +29 -27
- package/tests/tools/chat.test.ts +46 -20
- package/tests/tools/classroom.test.ts +39 -38
- package/tests/tools/contacts.test.ts +3 -2
- package/tests/tools/docs.test.ts +44 -15
- package/tests/tools/drive.test.ts +106 -19
- package/tests/tools/gmail.test.ts +227 -29
- package/tests/tools/run-tool-examples.test.ts +69 -0
- package/tests/tools/sheets.test.ts +16 -15
- package/tests/tools/slides.test.ts +47 -11
- package/tests/tools/tasks.test.ts +7 -6
- package/tests/tools/utils.test.ts +32 -31
- package/vitest.config.ts +5 -0
package/dist/index.js
CHANGED
|
@@ -2119,8 +2119,8 @@ function emoji() {
|
|
|
2119
2119
|
}
|
|
2120
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])$/;
|
|
2121
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}|:))$/;
|
|
2122
|
-
var mac = (
|
|
2123
|
-
const escapedDelim = escapeRegex(
|
|
2122
|
+
var mac = (delimiter3) => {
|
|
2123
|
+
const escapedDelim = escapeRegex(delimiter3 ?? ":");
|
|
2124
2124
|
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
|
|
2125
2125
|
};
|
|
2126
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])$/;
|
|
@@ -5148,7 +5148,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
|
|
|
5148
5148
|
var NONE = 0;
|
|
5149
5149
|
var ASSUMED = 1;
|
|
5150
5150
|
var PROVEN = 2;
|
|
5151
|
-
function isRecursive(inst, stack,
|
|
5151
|
+
function isRecursive(inst, stack, resolve4) {
|
|
5152
5152
|
const cached2 = recursive.get(inst);
|
|
5153
5153
|
if (cached2 !== void 0)
|
|
5154
5154
|
return cached2 ? PROVEN : NONE;
|
|
@@ -5158,7 +5158,7 @@ function isRecursive(inst, stack, resolve) {
|
|
|
5158
5158
|
let result = NONE;
|
|
5159
5159
|
const check2 = (child) => {
|
|
5160
5160
|
if (result !== PROVEN && child?._zod) {
|
|
5161
|
-
const answer = isRecursive(child, stack,
|
|
5161
|
+
const answer = isRecursive(child, stack, resolve4);
|
|
5162
5162
|
if (answer > result)
|
|
5163
5163
|
result = answer;
|
|
5164
5164
|
}
|
|
@@ -5169,7 +5169,7 @@ function isRecursive(inst, stack, resolve) {
|
|
|
5169
5169
|
const desc = Object.getOwnPropertyDescriptor(sh, key);
|
|
5170
5170
|
if (spread && !desc.enumerable)
|
|
5171
5171
|
continue;
|
|
5172
|
-
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;
|
|
5173
5173
|
if (child > answer)
|
|
5174
5174
|
answer = child;
|
|
5175
5175
|
}
|
|
@@ -5233,7 +5233,7 @@ function isRecursive(inst, stack, resolve) {
|
|
|
5233
5233
|
break;
|
|
5234
5234
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
5235
5235
|
case "lazy": {
|
|
5236
|
-
const inner = def._cachedInner ?? (
|
|
5236
|
+
const inner = def._cachedInner ?? (resolve4 ? inst._zod.innerType : void 0);
|
|
5237
5237
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
5238
5238
|
break;
|
|
5239
5239
|
}
|
|
@@ -24164,14 +24164,14 @@ function inputRequiredRoundsExceededMessage(method, maxRounds) {
|
|
|
24164
24164
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
24165
24165
|
}
|
|
24166
24166
|
function sleep(ms, signal) {
|
|
24167
|
-
return new Promise((
|
|
24167
|
+
return new Promise((resolve4, reject) => {
|
|
24168
24168
|
if (signal?.aborted) {
|
|
24169
24169
|
reject(signal.reason instanceof SdkError ? signal.reason : new SdkError(SdkErrorCode.RequestTimeout, String(signal.reason)));
|
|
24170
24170
|
return;
|
|
24171
24171
|
}
|
|
24172
24172
|
const timer = setTimeout(() => {
|
|
24173
24173
|
signal?.removeEventListener("abort", onAbort);
|
|
24174
|
-
|
|
24174
|
+
resolve4();
|
|
24175
24175
|
}, ms);
|
|
24176
24176
|
const onAbort = () => {
|
|
24177
24177
|
clearTimeout(timer);
|
|
@@ -24965,7 +24965,7 @@ var Protocol = class {
|
|
|
24965
24965
|
const flowStartedAt = Date.now();
|
|
24966
24966
|
let onAbort;
|
|
24967
24967
|
let cleanupMessageId;
|
|
24968
|
-
return new Promise((
|
|
24968
|
+
return new Promise((resolve4, reject) => {
|
|
24969
24969
|
const earlyReject = (error62) => {
|
|
24970
24970
|
reject(error62);
|
|
24971
24971
|
};
|
|
@@ -25033,7 +25033,7 @@ var Protocol = class {
|
|
|
25033
25033
|
}
|
|
25034
25034
|
if (decoded.kind === "invalid") return reject(decoded.error);
|
|
25035
25035
|
if (decoded.kind === "input_required") {
|
|
25036
|
-
if (options?.allowInputRequired === true) return
|
|
25036
|
+
if (options?.allowInputRequired === true) return resolve4(manualInputRequiredValue(decoded));
|
|
25037
25037
|
const flow = {
|
|
25038
25038
|
codec: codec2,
|
|
25039
25039
|
request,
|
|
@@ -25045,11 +25045,11 @@ var Protocol = class {
|
|
|
25045
25045
|
params
|
|
25046
25046
|
}, resultSchema, legOptions)
|
|
25047
25047
|
};
|
|
25048
|
-
return
|
|
25048
|
+
return resolve4(this._resolveNonCompleteResult(decoded, flow));
|
|
25049
25049
|
}
|
|
25050
25050
|
const result = decoded.result;
|
|
25051
25051
|
validateStandardSchema(resultSchema, result).then((parseResult) => {
|
|
25052
|
-
if (parseResult.success)
|
|
25052
|
+
if (parseResult.success) resolve4(parseResult.data);
|
|
25053
25053
|
else reject(new SdkError(SdkErrorCode.InvalidResult, `Invalid result for ${request.method}: ${parseResult.error}`));
|
|
25054
25054
|
}, reject);
|
|
25055
25055
|
});
|
|
@@ -27961,7 +27961,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
27961
27961
|
ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref);
|
|
27962
27962
|
const schOrFunc = root.refs[ref];
|
|
27963
27963
|
if (schOrFunc) return schOrFunc;
|
|
27964
|
-
let _sch =
|
|
27964
|
+
let _sch = resolve4.call(this, root, ref);
|
|
27965
27965
|
if (_sch === void 0) {
|
|
27966
27966
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
27967
27967
|
const { schemaId } = this.opts;
|
|
@@ -27987,7 +27987,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
27987
27987
|
function sameSchemaEnv(s1, s2) {
|
|
27988
27988
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
27989
27989
|
}
|
|
27990
|
-
function
|
|
27990
|
+
function resolve4(root, ref) {
|
|
27991
27991
|
let sch;
|
|
27992
27992
|
while (typeof (sch = this.refs[ref]) == "string") ref = sch;
|
|
27993
27993
|
return sch || this.schemas[ref] || resolveSchema.call(this, root, ref);
|
|
@@ -28437,47 +28437,47 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
28437
28437
|
else if (typeof uri === "object") uri = parse3(serialize(uri, options), options);
|
|
28438
28438
|
return uri;
|
|
28439
28439
|
}
|
|
28440
|
-
function
|
|
28440
|
+
function resolve4(baseURI, relativeURI, options) {
|
|
28441
28441
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
28442
28442
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
28443
28443
|
schemelessOptions.skipEscape = true;
|
|
28444
28444
|
return serialize(resolved, schemelessOptions);
|
|
28445
28445
|
}
|
|
28446
|
-
function resolveComponent(base,
|
|
28446
|
+
function resolveComponent(base, relative3, options, skipNormalization) {
|
|
28447
28447
|
const target = {};
|
|
28448
28448
|
if (!skipNormalization) {
|
|
28449
28449
|
base = parse3(serialize(base, options), options);
|
|
28450
|
-
|
|
28450
|
+
relative3 = parse3(serialize(relative3, options), options);
|
|
28451
28451
|
}
|
|
28452
28452
|
options = options || {};
|
|
28453
|
-
if (!options.tolerant &&
|
|
28454
|
-
target.scheme =
|
|
28455
|
-
target.userinfo =
|
|
28456
|
-
target.host =
|
|
28457
|
-
target.port =
|
|
28458
|
-
target.path = removeDotSegments(
|
|
28459
|
-
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;
|
|
28460
28460
|
} else {
|
|
28461
|
-
if (
|
|
28462
|
-
target.userinfo =
|
|
28463
|
-
target.host =
|
|
28464
|
-
target.port =
|
|
28465
|
-
target.path = removeDotSegments(
|
|
28466
|
-
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;
|
|
28467
28467
|
} else {
|
|
28468
|
-
if (!
|
|
28468
|
+
if (!relative3.path) {
|
|
28469
28469
|
target.path = base.path;
|
|
28470
|
-
if (
|
|
28470
|
+
if (relative3.query !== void 0) target.query = relative3.query;
|
|
28471
28471
|
else target.query = base.query;
|
|
28472
28472
|
} else {
|
|
28473
|
-
if (
|
|
28473
|
+
if (relative3.path[0] === "/") target.path = removeDotSegments(relative3.path);
|
|
28474
28474
|
else {
|
|
28475
|
-
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) target.path = "/" +
|
|
28476
|
-
else if (!base.path) target.path =
|
|
28477
|
-
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;
|
|
28478
28478
|
target.path = removeDotSegments(target.path);
|
|
28479
28479
|
}
|
|
28480
|
-
target.query =
|
|
28480
|
+
target.query = relative3.query;
|
|
28481
28481
|
}
|
|
28482
28482
|
target.userinfo = base.userinfo;
|
|
28483
28483
|
target.host = base.host;
|
|
@@ -28485,7 +28485,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
28485
28485
|
}
|
|
28486
28486
|
target.scheme = base.scheme;
|
|
28487
28487
|
}
|
|
28488
|
-
target.fragment =
|
|
28488
|
+
target.fragment = relative3.fragment;
|
|
28489
28489
|
return target;
|
|
28490
28490
|
}
|
|
28491
28491
|
function equal(uriA, uriB, options) {
|
|
@@ -28611,7 +28611,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
28611
28611
|
const fastUri = {
|
|
28612
28612
|
SCHEMES,
|
|
28613
28613
|
normalize,
|
|
28614
|
-
resolve,
|
|
28614
|
+
resolve: resolve4,
|
|
28615
28615
|
resolveComponent,
|
|
28616
28616
|
equal,
|
|
28617
28617
|
serialize,
|
|
@@ -29393,14 +29393,14 @@ var require_ucs2length = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
29393
29393
|
function ucs2length(str) {
|
|
29394
29394
|
const len = str.length;
|
|
29395
29395
|
let length = 0;
|
|
29396
|
-
let
|
|
29396
|
+
let pos2 = 0;
|
|
29397
29397
|
let value;
|
|
29398
|
-
while (
|
|
29398
|
+
while (pos2 < len) {
|
|
29399
29399
|
length++;
|
|
29400
|
-
value = str.charCodeAt(
|
|
29401
|
-
if (value >= 55296 && value <= 56319 &&
|
|
29402
|
-
value = str.charCodeAt(
|
|
29403
|
-
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++;
|
|
29404
29404
|
}
|
|
29405
29405
|
}
|
|
29406
29406
|
return length;
|
|
@@ -33984,7 +33984,7 @@ var StdioServerTransport = class {
|
|
|
33984
33984
|
}
|
|
33985
33985
|
send(message) {
|
|
33986
33986
|
if (this._closed) return Promise.reject(/* @__PURE__ */ new Error("StdioServerTransport is closed"));
|
|
33987
|
-
return new Promise((
|
|
33987
|
+
return new Promise((resolve4, reject) => {
|
|
33988
33988
|
const json2 = serializeMessage(message);
|
|
33989
33989
|
let settled = false;
|
|
33990
33990
|
const onError = (error62) => {
|
|
@@ -33999,14 +33999,14 @@ var StdioServerTransport = class {
|
|
|
33999
33999
|
settled = true;
|
|
34000
34000
|
this._stdout.off("error", onError);
|
|
34001
34001
|
this._stdout.off("drain", onDrain);
|
|
34002
|
-
|
|
34002
|
+
resolve4();
|
|
34003
34003
|
};
|
|
34004
34004
|
this._stdout.once("error", onError);
|
|
34005
34005
|
if (this._stdout.write(json2)) {
|
|
34006
34006
|
if (settled) return;
|
|
34007
34007
|
settled = true;
|
|
34008
34008
|
this._stdout.off("error", onError);
|
|
34009
|
-
|
|
34009
|
+
resolve4();
|
|
34010
34010
|
} else if (!settled) this._stdout.once("drain", onDrain);
|
|
34011
34011
|
});
|
|
34012
34012
|
}
|
|
@@ -34060,14 +34060,14 @@ var StdioConnectionChannel = class {
|
|
|
34060
34060
|
*/
|
|
34061
34061
|
async whenRequestsAnswered(timeoutMs) {
|
|
34062
34062
|
if (this._closed || this._pendingRequests.size === 0) return true;
|
|
34063
|
-
return await new Promise((
|
|
34063
|
+
return await new Promise((resolve4) => {
|
|
34064
34064
|
const waiter = () => {
|
|
34065
34065
|
clearTimeout(timer);
|
|
34066
|
-
|
|
34066
|
+
resolve4(true);
|
|
34067
34067
|
};
|
|
34068
34068
|
const timer = setTimeout(() => {
|
|
34069
34069
|
this._drainWaiters = this._drainWaiters.filter((pending) => pending !== waiter);
|
|
34070
|
-
|
|
34070
|
+
resolve4(false);
|
|
34071
34071
|
}, timeoutMs);
|
|
34072
34072
|
this._drainWaiters.push(waiter);
|
|
34073
34073
|
});
|
|
@@ -34437,13 +34437,28 @@ var API_KEY_RE = new RegExp([
|
|
|
34437
34437
|
"whsec_[A-Za-z0-9]{16,}\\b"
|
|
34438
34438
|
// webhook signing secret (Stripe-style)
|
|
34439
34439
|
].map((p) => `\\b${p}`).join("|"), "g");
|
|
34440
|
-
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;
|
|
34441
34444
|
var AWS_SIGV4_RE = /([?&]X-Amz-(?:Signature|Security-Token|Credential)=)[^&#\s"'<>`]+/gi;
|
|
34442
|
-
var JSON_SECRET_KEYS =
|
|
34443
|
-
|
|
34444
|
-
|
|
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");
|
|
34445
34460
|
function redactSecrets(text) {
|
|
34446
|
-
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]");
|
|
34447
34462
|
}
|
|
34448
34463
|
|
|
34449
34464
|
// ../../node_modules/@chrischall/mcp-utils/dist/response/view.js
|
|
@@ -34538,26 +34553,105 @@ function errorResult(message) {
|
|
|
34538
34553
|
}
|
|
34539
34554
|
|
|
34540
34555
|
// ../../node_modules/@chrischall/mcp-utils/dist/server/confirmation.js
|
|
34556
|
+
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
34541
34557
|
var DEFAULT_REQUEST_KEY = "confirmation";
|
|
34542
34558
|
var DEFAULT_CONFIRMATION_LABEL = "Confirm this action should proceed.";
|
|
34543
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
|
+
}
|
|
34544
34648
|
function requireConfirmation(ctx, options) {
|
|
34545
34649
|
const requestKey = options.requestKey ?? DEFAULT_REQUEST_KEY;
|
|
34650
|
+
const key = options.binding ? bindingKey(options.binding) : void 0;
|
|
34546
34651
|
const confirmationSchema = external_exports.object({
|
|
34547
34652
|
confirmed: external_exports.boolean().describe(options.confirmationLabel ?? DEFAULT_CONFIRMATION_LABEL)
|
|
34548
34653
|
});
|
|
34549
|
-
const
|
|
34550
|
-
const accepted = acceptedContent(ctx.mcpReq.inputResponses, requestKey, confirmationSchema);
|
|
34551
|
-
if (response.kind === "missing") {
|
|
34552
|
-
if (callerAcceptsFormElicitation(ctx) === false) {
|
|
34553
|
-
return textResult({
|
|
34554
|
-
confirmed: false,
|
|
34555
|
-
dispatched: false,
|
|
34556
|
-
action: options.action,
|
|
34557
|
-
reason: "confirmation-unsupported",
|
|
34558
|
-
note: options.unsupportedNote ? `${UNSUPPORTED_NOTE}. ${options.unsupportedNote}` : `${UNSUPPORTED_NOTE}.`
|
|
34559
|
-
});
|
|
34560
|
-
}
|
|
34654
|
+
const ask = () => {
|
|
34561
34655
|
const preview = {
|
|
34562
34656
|
action: options.action,
|
|
34563
34657
|
...options.details === void 0 ? {} : { details: options.details }
|
|
@@ -34569,10 +34663,33 @@ function requireConfirmation(ctx, options) {
|
|
|
34569
34663
|
${JSON.stringify(preview, null, 2)}`,
|
|
34570
34664
|
requestedSchema: confirmationSchema
|
|
34571
34665
|
})
|
|
34572
|
-
}
|
|
34666
|
+
},
|
|
34667
|
+
...key && options.binding ? { requestState: mintState(key, options.action, options.binding) } : {}
|
|
34573
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();
|
|
34574
34683
|
}
|
|
34575
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
|
+
}
|
|
34576
34693
|
return void 0;
|
|
34577
34694
|
}
|
|
34578
34695
|
return textResult({
|
|
@@ -34677,13 +34794,47 @@ async function createMcpServer(opts) {
|
|
|
34677
34794
|
}
|
|
34678
34795
|
return server;
|
|
34679
34796
|
}
|
|
34797
|
+
var DEFAULT_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
34798
|
+
var DEFAULT_REPEAT_SIGNAL_GRACE_MS = 500;
|
|
34680
34799
|
function withGracefulShutdown(target, opts = {}) {
|
|
34681
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;
|
|
34682
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
|
+
};
|
|
34683
34818
|
const handler = (signal) => {
|
|
34684
|
-
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
|
+
}
|
|
34685
34826
|
return;
|
|
34827
|
+
}
|
|
34686
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
|
+
}
|
|
34687
34838
|
void (async () => {
|
|
34688
34839
|
try {
|
|
34689
34840
|
if (opts.onSignal)
|
|
@@ -34692,8 +34843,10 @@ function withGracefulShutdown(target, opts = {}) {
|
|
|
34692
34843
|
} catch (err) {
|
|
34693
34844
|
console.error(`[mcp-utils] error during graceful shutdown on ${signal}: ${err instanceof Error ? err.message : String(err)}`);
|
|
34694
34845
|
} finally {
|
|
34846
|
+
if (timer !== void 0)
|
|
34847
|
+
clearTimeout(timer);
|
|
34695
34848
|
if (shouldExit)
|
|
34696
|
-
|
|
34849
|
+
exitOnce();
|
|
34697
34850
|
}
|
|
34698
34851
|
})();
|
|
34699
34852
|
};
|
|
@@ -34723,6 +34876,8 @@ function runMcp(opts) {
|
|
|
34723
34876
|
}
|
|
34724
34877
|
|
|
34725
34878
|
// ../../node_modules/@chrischall/mcp-utils/dist/config/index.js
|
|
34879
|
+
import { homedir } from "node:os";
|
|
34880
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
34726
34881
|
var PLACEHOLDER_RE = /^\$\{[^}]*\}$/;
|
|
34727
34882
|
function readEnvVar(key, opts = {}) {
|
|
34728
34883
|
const env = opts.env ?? process.env;
|
|
@@ -34749,6 +34904,50 @@ function parseBoolEnv(key, opts = {}) {
|
|
|
34749
34904
|
return false;
|
|
34750
34905
|
return fallback;
|
|
34751
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
|
+
}
|
|
34752
34951
|
|
|
34753
34952
|
// ../../node_modules/@chrischall/mcp-utils/dist/zod/index.js
|
|
34754
34953
|
var PositiveInt = external_exports.number().int().positive();
|
|
@@ -34770,7 +34969,7 @@ var pageSchema = {
|
|
|
34770
34969
|
};
|
|
34771
34970
|
|
|
34772
34971
|
// src/runner.ts
|
|
34773
|
-
import { delimiter, join } from "node:path";
|
|
34972
|
+
import { delimiter, join as join2 } from "node:path";
|
|
34774
34973
|
|
|
34775
34974
|
// src/timestamps.ts
|
|
34776
34975
|
var DEFAULT_DISPLAY_TZ = "America/New_York";
|
|
@@ -34971,9 +35170,48 @@ function normalizeTimestamps(text, tz = displayTimeZone(), naiveTz = naiveSource
|
|
|
34971
35170
|
return JSON.stringify(parsed, null, detectIndent(text));
|
|
34972
35171
|
}
|
|
34973
35172
|
|
|
35173
|
+
// 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
|
+
// 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
|
+
|
|
34974
35212
|
// src/runner.ts
|
|
34975
35213
|
function isGogFileArg(arg) {
|
|
34976
|
-
return typeof arg !== "string";
|
|
35214
|
+
return typeof arg !== "string" && arg.kind === "file";
|
|
34977
35215
|
}
|
|
34978
35216
|
var TIMEOUT_MS = 3e4;
|
|
34979
35217
|
function readonlyEnvEnabled() {
|
|
@@ -35057,8 +35295,8 @@ function formatTimeout(ms) {
|
|
|
35057
35295
|
}
|
|
35058
35296
|
async function spawnWithTempFiles(args, opts) {
|
|
35059
35297
|
const { mkdtemp, mkdir, writeFile, rm } = await import("node:fs/promises");
|
|
35060
|
-
const { tmpdir } = await import("node:os");
|
|
35061
|
-
const dir = await mkdtemp(
|
|
35298
|
+
const { tmpdir: tmpdir2 } = await import("node:os");
|
|
35299
|
+
const dir = await mkdtemp(join2(tmpdir2(), "gogcli-mcp-"));
|
|
35062
35300
|
try {
|
|
35063
35301
|
const argv = [];
|
|
35064
35302
|
let seq = 0;
|
|
@@ -35067,10 +35305,10 @@ async function spawnWithTempFiles(args, opts) {
|
|
|
35067
35305
|
argv.push(arg);
|
|
35068
35306
|
continue;
|
|
35069
35307
|
}
|
|
35070
|
-
const sub =
|
|
35308
|
+
const sub = join2(dir, String(seq));
|
|
35071
35309
|
seq += 1;
|
|
35072
35310
|
await mkdir(sub, { recursive: true, mode: 448 });
|
|
35073
|
-
const path =
|
|
35311
|
+
const path = join2(sub, arg.filename ?? `${arg.flag}.${arg.ext ?? "txt"}`);
|
|
35074
35312
|
const data = arg.encoding === "base64" ? Buffer.from(arg.contents, "base64") : Buffer.from(arg.contents, "utf8");
|
|
35075
35313
|
await writeFile(path, data, { mode: 384 });
|
|
35076
35314
|
argv.push(arg.positional ? path : `--${arg.flag}=${path}`);
|
|
@@ -35088,10 +35326,10 @@ function spawnExecutor(args, opts) {
|
|
|
35088
35326
|
return spawnGog(args, opts);
|
|
35089
35327
|
}
|
|
35090
35328
|
async function spawnGog(fullArgs, opts) {
|
|
35091
|
-
const { timeout, interactive = false, spawner, binary = false } = opts;
|
|
35329
|
+
const { timeout, interactive = false, spawner, binary = false, maxOutputBytes } = opts;
|
|
35092
35330
|
const spawn = spawner ?? (await import("node:child_process")).spawn;
|
|
35093
35331
|
const effectiveTimeout = timeout ?? TIMEOUT_MS;
|
|
35094
|
-
return new Promise((
|
|
35332
|
+
return new Promise((resolve4, reject) => {
|
|
35095
35333
|
const childEnv = { ...sanitizedEnv(), GOG_TIMEZONE: naiveSourceTimeZone(), PATH: augmentedPath() };
|
|
35096
35334
|
const child = spawn(readEnvVar("GOG_PATH") ?? "gog", fullArgs, { env: childEnv });
|
|
35097
35335
|
const stdoutChunks = [];
|
|
@@ -35109,7 +35347,17 @@ async function spawnGog(fullArgs, opts) {
|
|
|
35109
35347
|
child.kill();
|
|
35110
35348
|
reject(new Error(`gog timed out after ${formatTimeout(effectiveTimeout)}`));
|
|
35111
35349
|
}, effectiveTimeout);
|
|
35350
|
+
let stdoutBytes = 0;
|
|
35112
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
|
+
}
|
|
35113
35361
|
stdoutChunks.push(chunk);
|
|
35114
35362
|
});
|
|
35115
35363
|
child.stderr.on("data", (chunk) => {
|
|
@@ -35126,14 +35374,14 @@ async function spawnGog(fullArgs, opts) {
|
|
|
35126
35374
|
const stderr = Buffer.concat(stderrChunks).toString().trim();
|
|
35127
35375
|
if (code === 0) {
|
|
35128
35376
|
if (binary) {
|
|
35129
|
-
|
|
35377
|
+
resolve4(Buffer.concat(stdoutChunks).toString("base64"));
|
|
35130
35378
|
return;
|
|
35131
35379
|
}
|
|
35132
35380
|
const stdout = Buffer.concat(stdoutChunks).toString();
|
|
35133
35381
|
if (interactive && stderr) {
|
|
35134
|
-
|
|
35382
|
+
resolve4(stdout + "\n" + stderr);
|
|
35135
35383
|
} else {
|
|
35136
|
-
|
|
35384
|
+
resolve4(stdout);
|
|
35137
35385
|
}
|
|
35138
35386
|
} else {
|
|
35139
35387
|
reject(new Error(stderr || `gog exited with code ${code}`));
|
|
@@ -35153,7 +35401,33 @@ async function spawnGog(fullArgs, opts) {
|
|
|
35153
35401
|
});
|
|
35154
35402
|
});
|
|
35155
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
|
+
}
|
|
35156
35428
|
function assembleArgs(args, opts) {
|
|
35429
|
+
const { flags, positionals } = placePositionals(args);
|
|
35430
|
+
assertNoSafetyOverrides(flags);
|
|
35157
35431
|
const effectiveAccount = opts.account ?? readEnvVar("GOG_ACCOUNT");
|
|
35158
35432
|
const fullArgs = ["--json", "--color=never"];
|
|
35159
35433
|
if (!opts.interactive) {
|
|
@@ -35162,17 +35436,21 @@ function assembleArgs(args, opts) {
|
|
|
35162
35436
|
if (opts.readonly || readonlyEnvEnabled()) {
|
|
35163
35437
|
fullArgs.push("--readonly");
|
|
35164
35438
|
}
|
|
35439
|
+
if (opts.gmailNoSend) {
|
|
35440
|
+
fullArgs.push("--gmail-no-send");
|
|
35441
|
+
}
|
|
35165
35442
|
if (effectiveAccount) {
|
|
35166
35443
|
fullArgs.push("--account", effectiveAccount);
|
|
35167
35444
|
}
|
|
35168
|
-
fullArgs.push(...
|
|
35445
|
+
fullArgs.push(...flags);
|
|
35446
|
+
if (positionals.length > 0) fullArgs.push("--", ...positionals);
|
|
35169
35447
|
return fullArgs;
|
|
35170
35448
|
}
|
|
35171
35449
|
async function run(args, options = {}) {
|
|
35172
|
-
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;
|
|
35173
35451
|
const base = redactMode === "tokens" ? redactGoogleTokens : redactSecrets2;
|
|
35174
35452
|
const redact = opaqueFields?.length ? (text) => redactPreservingOpaqueFields(text, opaqueFields, base) : base;
|
|
35175
|
-
const fullArgs = assembleArgs(args, { account, interactive, readonly: readonly2 });
|
|
35453
|
+
const fullArgs = assembleArgs(args, { account, interactive, readonly: readonly2, gmailNoSend });
|
|
35176
35454
|
try {
|
|
35177
35455
|
return redact(await spawnExecutor(fullArgs, { timeout, interactive, spawner }));
|
|
35178
35456
|
} catch (err) {
|
|
@@ -35180,9 +35458,9 @@ async function run(args, options = {}) {
|
|
|
35180
35458
|
}
|
|
35181
35459
|
}
|
|
35182
35460
|
async function runBinary(args, options = {}) {
|
|
35183
|
-
const { account, spawner, timeout, readonly: readonly2 = false } = options;
|
|
35184
|
-
const fullArgs = assembleArgs(args, { account, interactive: false, readonly: readonly2 });
|
|
35185
|
-
return spawnExecutor(fullArgs, { timeout, interactive: false, spawner, binary: true });
|
|
35461
|
+
const { account, spawner, timeout, readonly: readonly2 = false, gmailNoSend = false, maxOutputBytes } = options;
|
|
35462
|
+
const fullArgs = assembleArgs(args, { account, interactive: false, readonly: readonly2, gmailNoSend });
|
|
35463
|
+
return spawnExecutor(fullArgs, { timeout, interactive: false, spawner, binary: true, maxOutputBytes });
|
|
35186
35464
|
}
|
|
35187
35465
|
|
|
35188
35466
|
// src/pagination.ts
|
|
@@ -35234,6 +35512,114 @@ function annotateTruncatedList(result, itemsKey) {
|
|
|
35234
35512
|
return rawTextResult(JSON.stringify(out));
|
|
35235
35513
|
}
|
|
35236
35514
|
|
|
35515
|
+
// src/file-roots.ts
|
|
35516
|
+
import { homedir as homedir2 } from "node:os";
|
|
35517
|
+
import { delimiter as delimiter2, join as join4 } from "node:path";
|
|
35518
|
+
|
|
35519
|
+
// src/attachment-root.ts
|
|
35520
|
+
import { tmpdir, userInfo } from "node:os";
|
|
35521
|
+
import { dirname as dirname2, join as join3, relative as relative2, resolve as resolve3, isAbsolute as isAbsolute3, sep as sep2 } from "node:path";
|
|
35522
|
+
var ATTACHMENT_DOWNLOAD_ROOT = join3(tmpdir(), `gogcli-mcp-attachments-${userInfo().uid}`);
|
|
35523
|
+
var ATTACHMENT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
35524
|
+
|
|
35525
|
+
// src/file-roots.ts
|
|
35526
|
+
var FILE_ROOTS_ENV = "GOG_FILE_ROOTS";
|
|
35527
|
+
function defaultFileRoot() {
|
|
35528
|
+
return join4(homedir2(), "gogcli-mcp-files");
|
|
35529
|
+
}
|
|
35530
|
+
function fileRoots() {
|
|
35531
|
+
const raw = readEnvVar(FILE_ROOTS_ENV);
|
|
35532
|
+
const roots = (raw ?? "").split(delimiter2).map((r) => r.trim()).filter(Boolean);
|
|
35533
|
+
return roots.length > 0 ? roots : [defaultFileRoot()];
|
|
35534
|
+
}
|
|
35535
|
+
function confinePath(path, param, opts = {}) {
|
|
35536
|
+
if (opts.allowDash && path === "-") return path;
|
|
35537
|
+
const roots = [...fileRoots(), ATTACHMENT_DOWNLOAD_ROOT];
|
|
35538
|
+
try {
|
|
35539
|
+
assertPathWithinRoots(path, roots);
|
|
35540
|
+
} catch {
|
|
35541
|
+
throw new Error(
|
|
35542
|
+
`${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}.`
|
|
35543
|
+
);
|
|
35544
|
+
}
|
|
35545
|
+
return path;
|
|
35546
|
+
}
|
|
35547
|
+
function confinePaths(paths, param) {
|
|
35548
|
+
for (const path of paths ?? []) confinePath(path, param);
|
|
35549
|
+
}
|
|
35550
|
+
function confineAtFile(value, param) {
|
|
35551
|
+
if (value.startsWith("@")) confinePath(value.slice(1), param);
|
|
35552
|
+
return value;
|
|
35553
|
+
}
|
|
35554
|
+
|
|
35555
|
+
// src/run-path-guard.ts
|
|
35556
|
+
var POSITIONAL_PATH_SUBCOMMANDS = {
|
|
35557
|
+
appscript: { pull: "gog_appscript_pull" },
|
|
35558
|
+
drive: { upload: "gog_drive_upload", sync: "gog_drive_sync_push" },
|
|
35559
|
+
gmail: { import: "gog_gmail_import" },
|
|
35560
|
+
slides: {
|
|
35561
|
+
"add-slide": "gog_slides_add_slide",
|
|
35562
|
+
"insert-image": "gog_slides_insert_image",
|
|
35563
|
+
"replace-slide": "gog_slides_replace_slide"
|
|
35564
|
+
}
|
|
35565
|
+
};
|
|
35566
|
+
var PATH_FLAGS = /* @__PURE__ */ new Set(["out", "output", "out-dir", "output-dir", "dir", "worker-dir", "attach", "file", "key", "cert", "replacements"]);
|
|
35567
|
+
var ID_FLAGS = {
|
|
35568
|
+
drive: /* @__PURE__ */ new Set(["file", "filter-file"])
|
|
35569
|
+
};
|
|
35570
|
+
var EXEC_FLAGS = /* @__PURE__ */ new Set(["on-change", "on-new", "mmdc"]);
|
|
35571
|
+
var SHORT_PATH_CLUSTER = /^-(?!-)[A-Za-z]*[of]/;
|
|
35572
|
+
var LONG_FLAG = /^--([A-Za-z0-9][A-Za-z0-9-]*)(?:=([\s\S]*))?$/;
|
|
35573
|
+
function isPathFlag(service, name) {
|
|
35574
|
+
if (ID_FLAGS[service]?.has(name)) return false;
|
|
35575
|
+
return PATH_FLAGS.has(name) || name.endsWith("-file");
|
|
35576
|
+
}
|
|
35577
|
+
function confineFlagValue(flag, value) {
|
|
35578
|
+
if (value === "-") return;
|
|
35579
|
+
confinePath(value, flag);
|
|
35580
|
+
for (const part of value.split(",")) {
|
|
35581
|
+
if (part !== "" && part !== "-") confinePath(part, flag);
|
|
35582
|
+
}
|
|
35583
|
+
}
|
|
35584
|
+
function positionalPathTool(service, subcommand) {
|
|
35585
|
+
return POSITIONAL_PATH_SUBCOMMANDS[service]?.[subcommand];
|
|
35586
|
+
}
|
|
35587
|
+
function assertRunPathsConfined(service, subcommand, args) {
|
|
35588
|
+
const dedicated = positionalPathTool(service, subcommand);
|
|
35589
|
+
if (dedicated) {
|
|
35590
|
+
throw new Error(
|
|
35591
|
+
`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.`
|
|
35592
|
+
);
|
|
35593
|
+
}
|
|
35594
|
+
for (let i = 0; i < args.length; i++) {
|
|
35595
|
+
const arg = args[i];
|
|
35596
|
+
if (SHORT_PATH_CLUSTER.test(arg)) {
|
|
35597
|
+
throw new Error(
|
|
35598
|
+
`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.`
|
|
35599
|
+
);
|
|
35600
|
+
}
|
|
35601
|
+
const match = LONG_FLAG.exec(arg);
|
|
35602
|
+
if (!match) continue;
|
|
35603
|
+
const name = match[1].toLowerCase();
|
|
35604
|
+
if (EXEC_FLAGS.has(name)) {
|
|
35605
|
+
throw new Error(`The flag --${name} is not allowed here: it runs a local program chosen by the caller.`);
|
|
35606
|
+
}
|
|
35607
|
+
const isJson = name.endsWith("-json");
|
|
35608
|
+
if (!isJson && !isPathFlag(service, name)) continue;
|
|
35609
|
+
let value = match[2];
|
|
35610
|
+
if (value === void 0) {
|
|
35611
|
+
value = args[i + 1];
|
|
35612
|
+
if (value === void 0) continue;
|
|
35613
|
+
i++;
|
|
35614
|
+
}
|
|
35615
|
+
if (isJson) {
|
|
35616
|
+
if (value.startsWith("@")) confineFlagValue(`--${name}`, value.slice(1));
|
|
35617
|
+
continue;
|
|
35618
|
+
}
|
|
35619
|
+
confineFlagValue(`--${name}`, value);
|
|
35620
|
+
}
|
|
35621
|
+
}
|
|
35622
|
+
|
|
35237
35623
|
// src/tools/utils.ts
|
|
35238
35624
|
var PAYLOAD_INLINE_MAX = 4096;
|
|
35239
35625
|
function payloadArg(inlineFlag, fileFlag, value, ext) {
|
|
@@ -35291,9 +35677,17 @@ function pushPaginationFlags(args, p) {
|
|
|
35291
35677
|
if (p.all) args.push("--all");
|
|
35292
35678
|
}
|
|
35293
35679
|
function registerRunTool(server, options) {
|
|
35294
|
-
const { service, examples, omitAccount = false, note } = options;
|
|
35680
|
+
const { service, examples, omitAccount = false, note, allowedSubcommands, vet, gmailNoSend = false } = options;
|
|
35681
|
+
for (const [, example] of examples.matchAll(/"([^"]+)"/g)) {
|
|
35682
|
+
const refused = allowedSubcommands && !allowedSubcommands.includes(example) || vet?.(example, []) || positionalPathTool(service, example);
|
|
35683
|
+
if (refused) {
|
|
35684
|
+
throw new Error(`gog_${service}_run example "${example}" is refused by the tool itself; list a subcommand it runs.`);
|
|
35685
|
+
}
|
|
35686
|
+
}
|
|
35295
35687
|
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.`;
|
|
35296
|
-
const
|
|
35688
|
+
const restriction = allowedSubcommands ? ` Only these subcommands are available: ${allowedSubcommands.join(", ")}.` : "";
|
|
35689
|
+
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.';
|
|
35690
|
+
const description = `${baseDescription}${restriction}${safety}${note ? ` ${note}` : ""}`;
|
|
35297
35691
|
const inputSchema = {
|
|
35298
35692
|
subcommand: external_exports.string().describe(`The gog ${service} subcommand to run, e.g. ${examples}`),
|
|
35299
35693
|
args: external_exports.array(external_exports.string()).describe("Additional positional args and flags")
|
|
@@ -35307,7 +35701,21 @@ function registerRunTool(server, options) {
|
|
|
35307
35701
|
inputSchema: external_exports.object(inputSchema)
|
|
35308
35702
|
}, async (rawArgs) => {
|
|
35309
35703
|
const { subcommand, args, account } = rawArgs;
|
|
35310
|
-
|
|
35704
|
+
try {
|
|
35705
|
+
assertSafeSubcommand(subcommand);
|
|
35706
|
+
assertSafeForwardedArgs(args);
|
|
35707
|
+
if (allowedSubcommands && !allowedSubcommands.includes(subcommand)) {
|
|
35708
|
+
throw new Error(
|
|
35709
|
+
`gog ${service} ${subcommand} is not available through gog_${service}_run. Allowed: ${allowedSubcommands.join(", ")}.`
|
|
35710
|
+
);
|
|
35711
|
+
}
|
|
35712
|
+
const refusal = vet?.(subcommand, args);
|
|
35713
|
+
if (refusal) throw new Error(refusal);
|
|
35714
|
+
assertRunPathsConfined(service, subcommand, args);
|
|
35715
|
+
} catch (err) {
|
|
35716
|
+
return errorResult(errorText(err));
|
|
35717
|
+
}
|
|
35718
|
+
return runOrDiagnose([service, subcommand, ...args], gmailNoSend ? { account, gmailNoSend } : { account });
|
|
35311
35719
|
});
|
|
35312
35720
|
}
|
|
35313
35721
|
function errorText(err) {
|
|
@@ -35436,6 +35844,13 @@ function assertNotBoth(inlineParam, fileParam, inlineValue, fileValue) {
|
|
|
35436
35844
|
}
|
|
35437
35845
|
|
|
35438
35846
|
// src/tools/api.ts
|
|
35847
|
+
var GMAIL_API_BLOCKED = /(?:\.send$|forwarding|filters\.create|filters\.update|delegates\.create)/i;
|
|
35848
|
+
function refusedApiCall(api, method) {
|
|
35849
|
+
if (api.trim().toLowerCase() === "gmail" && GMAIL_API_BLOCKED.test(method.trim())) {
|
|
35850
|
+
return `gmail ${method} is not available through gog_api_call: it sends or forwards mail. Use gog_gmail_send / gog_gmail_drafts_send (which ask the user to confirm) or the dedicated gog_gmail_* tool.`;
|
|
35851
|
+
}
|
|
35852
|
+
return void 0;
|
|
35853
|
+
}
|
|
35439
35854
|
function registerApiTools(server) {
|
|
35440
35855
|
server.registerTool("gog_api_list", {
|
|
35441
35856
|
description: "List the Google Discovery APIs available for gog_api_call / gog_api_describe (name + version + title).",
|
|
@@ -35459,12 +35874,12 @@ function registerApiTools(server) {
|
|
|
35459
35874
|
account: accountParam
|
|
35460
35875
|
})
|
|
35461
35876
|
}, async ({ api, version: version2, method, account }) => {
|
|
35462
|
-
const args = ["api", "describe", api, version2];
|
|
35463
|
-
if (method) args.push(method);
|
|
35877
|
+
const args = ["api", "describe", pos(api), pos(version2)];
|
|
35878
|
+
if (method) args.push(pos(method));
|
|
35464
35879
|
return runOrDiagnose(args, { account });
|
|
35465
35880
|
});
|
|
35466
35881
|
server.registerTool("gog_api_call", {
|
|
35467
|
-
description: "Call any Discovery-described Google API method directly \u2014 an escape hatch for endpoints gog has no dedicated tool for. Find the exact api/version/method/params with gog_api_describe first. Read methods (GET/LIST) run as-is. Mutating methods (POST/PUT/PATCH/DELETE) are refused unless you set allowWrite=true \u2014 keep it false to preview, or set dryRun=true to print the intended request without sending it.",
|
|
35882
|
+
description: "Call any Discovery-described Google API method directly \u2014 an escape hatch for endpoints gog has no dedicated tool for. Find the exact api/version/method/params with gog_api_describe first. Read methods (GET/LIST) run as-is. Mutating methods (POST/PUT/PATCH/DELETE) are refused unless you set allowWrite=true \u2014 keep it false to preview, or set dryRun=true to print the intended request without sending it. Gmail send and forwarding methods (users.messages.send, users.drafts.send, forwarding/auto-forwarding, filters, delegates) are refused \u2014 use the dedicated gog_gmail_* tools, which ask the user to confirm.",
|
|
35468
35883
|
annotations: { destructiveHint: true },
|
|
35469
35884
|
inputSchema: external_exports.object({
|
|
35470
35885
|
api: external_exports.string().describe("Discovery API name (e.g. drive, gmail, calendar)"),
|
|
@@ -35478,14 +35893,23 @@ function registerApiTools(server) {
|
|
|
35478
35893
|
account: accountParam
|
|
35479
35894
|
})
|
|
35480
35895
|
}, async ({ api, version: version2, method, params, body, scope, allowWrite, dryRun, account }) => {
|
|
35481
|
-
|
|
35896
|
+
try {
|
|
35897
|
+
assertSafeForwardedArgs([api, version2, method]);
|
|
35898
|
+
const refusal = refusedApiCall(api, method);
|
|
35899
|
+
if (refusal) throw new Error(refusal);
|
|
35900
|
+
if (body) confineAtFile(body, "body");
|
|
35901
|
+
if (params) confineAtFile(params, "params");
|
|
35902
|
+
} catch (err) {
|
|
35903
|
+
return errorResult(errorText(err));
|
|
35904
|
+
}
|
|
35905
|
+
const args = ["api", "call", pos(api), pos(version2), pos(method)];
|
|
35482
35906
|
if (params) args.push(`--params=${params}`);
|
|
35483
35907
|
if (body) args.push(`--body=${body}`);
|
|
35484
35908
|
if (scope) args.push(`--scope=${scope}`);
|
|
35485
35909
|
if (allowWrite) args.push("--allow-write");
|
|
35486
35910
|
if (dryRun) args.push("--dry-run");
|
|
35487
35911
|
if (allowWrite) args.push("--force");
|
|
35488
|
-
return runOrDiagnose(args, { account });
|
|
35912
|
+
return runOrDiagnose(args, { account, gmailNoSend: true });
|
|
35489
35913
|
});
|
|
35490
35914
|
}
|
|
35491
35915
|
|
|
@@ -35503,7 +35927,7 @@ function registerAppScriptTools(server) {
|
|
|
35503
35927
|
account: accountParam
|
|
35504
35928
|
})
|
|
35505
35929
|
}, async ({ scriptId, account }) => {
|
|
35506
|
-
return runOrDiagnose(["appscript", "get", scriptId], { account });
|
|
35930
|
+
return runOrDiagnose(["appscript", "get", pos(scriptId)], { account });
|
|
35507
35931
|
});
|
|
35508
35932
|
server.registerTool("gog_appscript_content", {
|
|
35509
35933
|
description: `Read a project's source \u2014 every .gs file and its appsscript.json manifest \u2014 INLINE in the response. This is the tool to reach for when the question is "what does this script do"; it needs no filesystem, so it works the same on a hosted deployment as it does locally, unlike gog_appscript_pull.` + apiEnableNote,
|
|
@@ -35513,19 +35937,20 @@ function registerAppScriptTools(server) {
|
|
|
35513
35937
|
account: accountParam
|
|
35514
35938
|
})
|
|
35515
35939
|
}, async ({ scriptId, account }) => {
|
|
35516
|
-
return runOrDiagnose(["appscript", "content", scriptId], { account });
|
|
35940
|
+
return runOrDiagnose(["appscript", "content", pos(scriptId)], { account });
|
|
35517
35941
|
});
|
|
35518
35942
|
server.registerTool("gog_appscript_pull", {
|
|
35519
35943
|
description: "Write a project's files into a local directory, for editing a script as ordinary files. THE DIRECTORY IS RESOLVED WHERE GOG RUNS, which is the caller's own machine only on a local (stdio) deployment: on a hosted deployment (e.g. mcp-host) the files land on that server where the caller cannot reach them. Use gog_appscript_content there instead \u2014 it returns the same source in the response. Existing files are left alone unless overwrite is set. Read-only as far as Google is concerned: nothing is pushed back." + apiEnableNote,
|
|
35520
35944
|
annotations: { destructiveHint: true },
|
|
35521
35945
|
inputSchema: external_exports.object({
|
|
35522
35946
|
scriptId: scriptIdParam,
|
|
35523
|
-
dir: external_exports.string().describe("Destination directory, resolved on the machine where gog runs"),
|
|
35947
|
+
dir: external_exports.string().describe("Destination directory, resolved on the machine where gog runs; must be inside the server's GOG_FILE_ROOTS directories"),
|
|
35524
35948
|
overwrite: external_exports.boolean().optional().describe("Overwrite files that already exist in dir"),
|
|
35525
35949
|
account: accountParam
|
|
35526
35950
|
})
|
|
35527
35951
|
}, async ({ scriptId, dir, overwrite, account }) => {
|
|
35528
|
-
|
|
35952
|
+
confinePath(dir, "dir");
|
|
35953
|
+
const args = ["appscript", "pull", pos(scriptId), pos(dir)];
|
|
35529
35954
|
if (overwrite) args.push("--overwrite");
|
|
35530
35955
|
return runOrDiagnose(args, { account });
|
|
35531
35956
|
});
|
|
@@ -35551,7 +35976,7 @@ function registerAppScriptTools(server) {
|
|
|
35551
35976
|
account: accountParam
|
|
35552
35977
|
})
|
|
35553
35978
|
}, async ({ scriptId, max, pageToken, page, all, account }) => {
|
|
35554
|
-
const args = ["appscript", "deployments", scriptId];
|
|
35979
|
+
const args = ["appscript", "deployments", pos(scriptId)];
|
|
35555
35980
|
pushPaginationFlags(args, { max, pageToken, page, all });
|
|
35556
35981
|
return runOrDiagnose(args, { account });
|
|
35557
35982
|
});
|
|
@@ -35564,7 +35989,7 @@ function registerAppScriptTools(server) {
|
|
|
35564
35989
|
account: accountParam
|
|
35565
35990
|
})
|
|
35566
35991
|
}, async ({ scriptId, max, pageToken, page, all, account }) => {
|
|
35567
|
-
const args = ["appscript", "versions", scriptId];
|
|
35992
|
+
const args = ["appscript", "versions", pos(scriptId)];
|
|
35568
35993
|
pushPaginationFlags(args, { max, pageToken, page, all });
|
|
35569
35994
|
return runOrDiagnose(args, { account });
|
|
35570
35995
|
});
|
|
@@ -35590,7 +36015,7 @@ function registerAppScriptTools(server) {
|
|
|
35590
36015
|
throw new Error(`params must be a JSON ARRAY of positional arguments, e.g. '["a", 1]' \u2014 Apps Script takes positional arguments, not named ones. Received: ${params}`);
|
|
35591
36016
|
}
|
|
35592
36017
|
}
|
|
35593
|
-
const args = ["appscript", "run", scriptId, functionName];
|
|
36018
|
+
const args = ["appscript", "run", pos(scriptId), pos(functionName)];
|
|
35594
36019
|
if (params !== void 0) args.push(`--params=${params}`);
|
|
35595
36020
|
if (devMode) args.push("--dev-mode");
|
|
35596
36021
|
return runOrDiagnose(args, { account });
|
|
@@ -35603,6 +36028,7 @@ function registerAppScriptTools(server) {
|
|
|
35603
36028
|
}
|
|
35604
36029
|
|
|
35605
36030
|
// src/tools/auth.ts
|
|
36031
|
+
var AUTH_RUN_SUBCOMMANDS = ["list", "status", "services", "remove", "alias"];
|
|
35606
36032
|
function registerAuthToolsWith(server, defaultServices) {
|
|
35607
36033
|
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.`;
|
|
35608
36034
|
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.";
|
|
@@ -35660,7 +36086,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
35660
36086
|
})
|
|
35661
36087
|
}, async ({ email: email3, services = defaultServices, extraScopes }) => {
|
|
35662
36088
|
try {
|
|
35663
|
-
const args = ["auth", "add", email3, "--services", services];
|
|
36089
|
+
const args = ["auth", "add", pos(email3), "--services", services];
|
|
35664
36090
|
if (extraScopes) args.push(`--extra-scopes=${extraScopes}`, "--force-consent");
|
|
35665
36091
|
return rawTextResult(await run(args, {
|
|
35666
36092
|
interactive: true,
|
|
@@ -35680,7 +36106,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
35680
36106
|
})
|
|
35681
36107
|
}, async ({ email: email3, services = defaultServices, extraScopes }) => {
|
|
35682
36108
|
try {
|
|
35683
|
-
const args = ["auth", "add", email3, "--remote", "--step", "1", "--services", services, "--force-consent"];
|
|
36109
|
+
const args = ["auth", "add", pos(email3), "--remote", "--step", "1", "--services", services, "--force-consent"];
|
|
35684
36110
|
if (extraScopes) args.push(`--extra-scopes=${extraScopes}`);
|
|
35685
36111
|
return rawTextResult(await run(args, { redactMode: "tokens" }));
|
|
35686
36112
|
} catch (err) {
|
|
@@ -35707,7 +36133,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
35707
36133
|
const args = [
|
|
35708
36134
|
"auth",
|
|
35709
36135
|
"add",
|
|
35710
|
-
email3,
|
|
36136
|
+
pos(email3),
|
|
35711
36137
|
"--remote",
|
|
35712
36138
|
"--step",
|
|
35713
36139
|
"2",
|
|
@@ -35727,6 +36153,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
35727
36153
|
service: "auth",
|
|
35728
36154
|
examples: '"remove", "alias", "list"',
|
|
35729
36155
|
omitAccount: true,
|
|
36156
|
+
allowedSubcommands: AUTH_RUN_SUBCOMMANDS,
|
|
35730
36157
|
note: "For browser-based authorization, use gog_auth_add instead."
|
|
35731
36158
|
});
|
|
35732
36159
|
}
|
|
@@ -35788,7 +36215,7 @@ function registerCalendarTools(server) {
|
|
|
35788
36215
|
})
|
|
35789
36216
|
}, async ({ calendarId, from, to, days, today, query, max, pageToken, page, all, eventTypes, timezone, view, account }) => {
|
|
35790
36217
|
const args = ["calendar", "events"];
|
|
35791
|
-
if (calendarId) args.push(calendarId);
|
|
36218
|
+
if (calendarId) args.push(pos(calendarId));
|
|
35792
36219
|
if (from) args.push(`--from=${from}`);
|
|
35793
36220
|
if (to) args.push(`--to=${to}`);
|
|
35794
36221
|
if (days !== void 0) args.push(`--days=${days}`);
|
|
@@ -35817,7 +36244,7 @@ function registerCalendarTools(server) {
|
|
|
35817
36244
|
account: accountParam
|
|
35818
36245
|
})
|
|
35819
36246
|
}, async ({ calendarId, eventId, timezone, account }) => {
|
|
35820
|
-
const args = ["calendar", "event", calendarId, eventId];
|
|
36247
|
+
const args = ["calendar", "event", pos(calendarId), pos(eventId)];
|
|
35821
36248
|
if (timezone) args.push(`--timezone=${timezone}`);
|
|
35822
36249
|
return runOrDiagnose(args, { account });
|
|
35823
36250
|
});
|
|
@@ -35839,7 +36266,7 @@ function registerCalendarTools(server) {
|
|
|
35839
36266
|
account: accountParam
|
|
35840
36267
|
})
|
|
35841
36268
|
}, async ({ calendarId, summary, from, to, description, location, attendees, allDay, timezone, withZoom, reminders, noReminders, account }) => {
|
|
35842
|
-
const args = ["calendar", "create", calendarId, `--summary=${summary}`, `--from=${from}`, `--to=${to}`];
|
|
36269
|
+
const args = ["calendar", "create", pos(calendarId), `--summary=${summary}`, `--from=${from}`, `--to=${to}`];
|
|
35843
36270
|
if (description) args.push(`--description=${description}`);
|
|
35844
36271
|
if (location) args.push(`--location=${location}`);
|
|
35845
36272
|
if (attendees) args.push(`--attendees=${attendees}`);
|
|
@@ -35871,7 +36298,7 @@ function registerCalendarTools(server) {
|
|
|
35871
36298
|
account: accountParam
|
|
35872
36299
|
})
|
|
35873
36300
|
}, async ({ calendarId, eventId, summary, from, to, description, location, attendees, addAttendees, attachments, withZoom, regenerateZoom, removeZoom, removeMeet, reminders, noReminders, account }) => {
|
|
35874
|
-
const args = ["calendar", "update", calendarId, eventId];
|
|
36301
|
+
const args = ["calendar", "update", pos(calendarId), pos(eventId)];
|
|
35875
36302
|
if (summary !== void 0) args.push(`--summary=${summary}`);
|
|
35876
36303
|
if (from !== void 0) args.push(`--from=${from}`);
|
|
35877
36304
|
if (to !== void 0) args.push(`--to=${to}`);
|
|
@@ -35896,7 +36323,7 @@ function registerCalendarTools(server) {
|
|
|
35896
36323
|
account: accountParam
|
|
35897
36324
|
})
|
|
35898
36325
|
}, async ({ calendarId, eventId, account }) => {
|
|
35899
|
-
return runOrDiagnose(["calendar", "delete", calendarId, eventId, "--force"], { account });
|
|
36326
|
+
return runOrDiagnose(["calendar", "delete", pos(calendarId), pos(eventId), "--force"], { account });
|
|
35900
36327
|
});
|
|
35901
36328
|
server.registerTool("gog_calendar_respond", {
|
|
35902
36329
|
description: "Respond to a calendar event invitation.",
|
|
@@ -35909,7 +36336,7 @@ function registerCalendarTools(server) {
|
|
|
35909
36336
|
account: accountParam
|
|
35910
36337
|
})
|
|
35911
36338
|
}, async ({ calendarId, eventId, status, comment, account }) => {
|
|
35912
|
-
const args = ["calendar", "respond", calendarId, eventId, `--status=${status}`];
|
|
36339
|
+
const args = ["calendar", "respond", pos(calendarId), pos(eventId), `--status=${status}`];
|
|
35913
36340
|
if (comment) args.push(`--comment=${comment}`);
|
|
35914
36341
|
return runOrDiagnose(args, { account });
|
|
35915
36342
|
});
|
|
@@ -35924,6 +36351,7 @@ var MAX_REQUEST_PAYLOAD_WIRE_BYTES = REQUEST_MAX_BODY_BYTES - REQUEST_BODY_JSON_
|
|
|
35924
36351
|
var MAX_INLINE_ATTACHMENT_TOTAL_BYTES = Math.floor(MAX_REQUEST_PAYLOAD_WIRE_BYTES * 3 / 4);
|
|
35925
36352
|
function wireBytesOf(arg) {
|
|
35926
36353
|
if (typeof arg === "string") return Buffer.byteLength(arg, "utf8");
|
|
36354
|
+
if (arg.kind === "positional") return Buffer.byteLength(arg.value, "utf8");
|
|
35927
36355
|
return arg.encoding === "base64" ? arg.contents.length : Buffer.byteLength(arg.contents, "utf8");
|
|
35928
36356
|
}
|
|
35929
36357
|
var formatMiB = (bytes) => `${Math.floor(bytes / (1024 * 1024))} MiB`;
|
|
@@ -36026,7 +36454,7 @@ function registerChatTools(server) {
|
|
|
36026
36454
|
account: accountParam
|
|
36027
36455
|
})
|
|
36028
36456
|
}, async ({ displayName, exact, max, account }) => {
|
|
36029
|
-
const args = ["chat", "spaces", "find", displayName];
|
|
36457
|
+
const args = ["chat", "spaces", "find", pos(displayName)];
|
|
36030
36458
|
if (exact) args.push("--exact");
|
|
36031
36459
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
36032
36460
|
return runOrDiagnose(args, { account });
|
|
@@ -36040,7 +36468,7 @@ function registerChatTools(server) {
|
|
|
36040
36468
|
account: accountParam
|
|
36041
36469
|
})
|
|
36042
36470
|
}, async ({ displayName, members: members2, account }) => {
|
|
36043
|
-
const args = ["chat", "spaces", "create", displayName];
|
|
36471
|
+
const args = ["chat", "spaces", "create", pos(displayName)];
|
|
36044
36472
|
if (members2) for (const member of members2) args.push(`--member=${member}`);
|
|
36045
36473
|
return runOrDiagnose(args, { account });
|
|
36046
36474
|
});
|
|
@@ -36053,7 +36481,7 @@ function registerChatTools(server) {
|
|
|
36053
36481
|
account: accountParam
|
|
36054
36482
|
})
|
|
36055
36483
|
}, async ({ space, max, pageToken, page, all, account }) => {
|
|
36056
|
-
const args = ["chat", "threads", "list", space];
|
|
36484
|
+
const args = ["chat", "threads", "list", pos(space)];
|
|
36057
36485
|
pushPaginationFlags(args, { max, pageToken, page, all });
|
|
36058
36486
|
return runOrDiagnose(args, { account });
|
|
36059
36487
|
});
|
|
@@ -36069,7 +36497,7 @@ function registerChatTools(server) {
|
|
|
36069
36497
|
account: accountParam
|
|
36070
36498
|
})
|
|
36071
36499
|
}, async ({ space, thread, unread, order, max, pageToken, page, all, account }) => {
|
|
36072
|
-
const args = ["chat", "messages", "list", space];
|
|
36500
|
+
const args = ["chat", "messages", "list", pos(space)];
|
|
36073
36501
|
if (thread) args.push(`--thread=${thread}`);
|
|
36074
36502
|
if (unread) args.push("--unread");
|
|
36075
36503
|
if (order) args.push(`--order=${order}`);
|
|
@@ -36093,7 +36521,7 @@ function registerChatTools(server) {
|
|
|
36093
36521
|
account: accountParam
|
|
36094
36522
|
})
|
|
36095
36523
|
}, async ({ query, order, view, markup, max, pageToken, page, all, account }) => {
|
|
36096
|
-
const args = ["chat", "messages", "search", query];
|
|
36524
|
+
const args = ["chat", "messages", "search", pos(query)];
|
|
36097
36525
|
if (order) args.push(`--order=${order}`);
|
|
36098
36526
|
if (view) args.push(`--view=${view}`);
|
|
36099
36527
|
if (markup) args.push(`--markup=${markup}`);
|
|
@@ -36108,16 +36536,17 @@ function registerChatTools(server) {
|
|
|
36108
36536
|
text: external_exports.string().optional().describe("Message text. Optional only when an attachment is supplied."),
|
|
36109
36537
|
thread: threadParam,
|
|
36110
36538
|
attach: external_exports.array(external_exports.string()).optional().describe(
|
|
36111
|
-
"Attachment file paths, read WHERE GOG RUNS. On a hosted or remote deployment that is not your machine \u2014 use attachInline there instead."
|
|
36539
|
+
"Attachment file paths, read WHERE GOG RUNS. On a hosted or remote deployment that is not your machine \u2014 use attachInline there instead. Must be inside the server's GOG_FILE_ROOTS directories (default ~/gogcli-mcp-files)."
|
|
36112
36540
|
),
|
|
36113
36541
|
attachInline: attachInlineParam,
|
|
36114
36542
|
account: accountParam
|
|
36115
36543
|
})
|
|
36116
36544
|
}, async ({ space, text, thread, attach, attachInline, account }) => {
|
|
36545
|
+
confinePaths(attach, "attach");
|
|
36117
36546
|
if (text === void 0 && !attach?.length && !attachInline?.length) {
|
|
36118
36547
|
throw new Error("A Chat message needs text, an attachment, or both.");
|
|
36119
36548
|
}
|
|
36120
|
-
const args = ["chat", "messages", "send", space];
|
|
36549
|
+
const args = ["chat", "messages", "send", pos(space)];
|
|
36121
36550
|
if (text !== void 0) args.push(`--text=${text}`);
|
|
36122
36551
|
if (thread) args.push(`--thread=${thread}`);
|
|
36123
36552
|
if (attach) for (const path of attach) args.push(`--attach=${path}`);
|
|
@@ -36134,7 +36563,7 @@ function registerChatTools(server) {
|
|
|
36134
36563
|
account: accountParam
|
|
36135
36564
|
})
|
|
36136
36565
|
}, async ({ email: email3, text, thread, account }) => {
|
|
36137
|
-
const args = ["chat", "dm", "send", email3, `--text=${text}`];
|
|
36566
|
+
const args = ["chat", "dm", "send", pos(email3), `--text=${text}`];
|
|
36138
36567
|
if (thread) args.push(`--thread=${thread}`);
|
|
36139
36568
|
return runOrDiagnose(args, { account });
|
|
36140
36569
|
});
|
|
@@ -36146,7 +36575,7 @@ function registerChatTools(server) {
|
|
|
36146
36575
|
account: accountParam
|
|
36147
36576
|
})
|
|
36148
36577
|
}, async ({ email: email3, account }) => {
|
|
36149
|
-
return runOrDiagnose(["chat", "dm", "space", email3], { account });
|
|
36578
|
+
return runOrDiagnose(["chat", "dm", "space", pos(email3)], { account });
|
|
36150
36579
|
});
|
|
36151
36580
|
server.registerTool("gog_chat_reactions_list", {
|
|
36152
36581
|
description: "List the emoji reactions on one message, with who reacted. gog_chat_messages_list already returns a reaction SUMMARY per message; come here when you need the individual reactors, or the reaction resource names that gog_chat_reactions_delete takes." + workspaceOnlyNote,
|
|
@@ -36158,7 +36587,7 @@ function registerChatTools(server) {
|
|
|
36158
36587
|
account: accountParam
|
|
36159
36588
|
})
|
|
36160
36589
|
}, async ({ message, space, max, pageToken, page, all, account }) => {
|
|
36161
|
-
const args = ["chat", "messages", "reactions", "list", message];
|
|
36590
|
+
const args = ["chat", "messages", "reactions", "list", pos(message)];
|
|
36162
36591
|
if (space) args.push(`--space=${space}`);
|
|
36163
36592
|
pushPaginationFlags(args, { max, pageToken, page, all });
|
|
36164
36593
|
return runOrDiagnose(args, { account });
|
|
@@ -36173,7 +36602,7 @@ function registerChatTools(server) {
|
|
|
36173
36602
|
account: accountParam
|
|
36174
36603
|
})
|
|
36175
36604
|
}, async ({ message, emoji: emoji3, space, account }) => {
|
|
36176
|
-
const args = ["chat", "messages", "reactions", "create", message, emoji3];
|
|
36605
|
+
const args = ["chat", "messages", "reactions", "create", pos(message), pos(emoji3)];
|
|
36177
36606
|
if (space) args.push(`--space=${space}`);
|
|
36178
36607
|
return runOrDiagnose(args, { account });
|
|
36179
36608
|
});
|
|
@@ -36185,7 +36614,7 @@ function registerChatTools(server) {
|
|
|
36185
36614
|
account: accountParam
|
|
36186
36615
|
})
|
|
36187
36616
|
}, async ({ reaction, account }) => {
|
|
36188
|
-
return runOrDiagnose(["chat", "messages", "reactions", "delete", reaction], { account });
|
|
36617
|
+
return runOrDiagnose(["chat", "messages", "reactions", "delete", pos(reaction)], { account });
|
|
36189
36618
|
});
|
|
36190
36619
|
registerRunTool(server, {
|
|
36191
36620
|
service: "chat",
|
|
@@ -36228,7 +36657,7 @@ function registerClassroomTools(server) {
|
|
|
36228
36657
|
account: accountParam
|
|
36229
36658
|
})
|
|
36230
36659
|
}, async ({ courseId, account }) => {
|
|
36231
|
-
return runOrDiagnose(["classroom", "courses", "get", courseId], { account });
|
|
36660
|
+
return runOrDiagnose(["classroom", "courses", "get", pos(courseId)], { account });
|
|
36232
36661
|
});
|
|
36233
36662
|
server.registerTool("gog_classroom_students_list", {
|
|
36234
36663
|
description: "List students enrolled in a Google Classroom course.",
|
|
@@ -36242,7 +36671,7 @@ function registerClassroomTools(server) {
|
|
|
36242
36671
|
account: accountParam
|
|
36243
36672
|
})
|
|
36244
36673
|
}, async ({ courseId, max, pageToken, page, all, account }) => {
|
|
36245
|
-
const args = ["classroom", "students", "list", courseId];
|
|
36674
|
+
const args = ["classroom", "students", "list", pos(courseId)];
|
|
36246
36675
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
36247
36676
|
const token = resolvePageToken({ pageToken, page });
|
|
36248
36677
|
if (token) args.push(`--page=${token}`);
|
|
@@ -36258,7 +36687,7 @@ function registerClassroomTools(server) {
|
|
|
36258
36687
|
account: accountParam
|
|
36259
36688
|
})
|
|
36260
36689
|
}, async ({ courseId, userId, account }) => {
|
|
36261
|
-
return runOrDiagnose(["classroom", "students", "get", courseId, userId], { account });
|
|
36690
|
+
return runOrDiagnose(["classroom", "students", "get", pos(courseId), pos(userId)], { account });
|
|
36262
36691
|
});
|
|
36263
36692
|
server.registerTool("gog_classroom_teachers_list", {
|
|
36264
36693
|
description: "List teachers in a Google Classroom course.",
|
|
@@ -36272,7 +36701,7 @@ function registerClassroomTools(server) {
|
|
|
36272
36701
|
account: accountParam
|
|
36273
36702
|
})
|
|
36274
36703
|
}, async ({ courseId, max, pageToken, page, all, account }) => {
|
|
36275
|
-
const args = ["classroom", "teachers", "list", courseId];
|
|
36704
|
+
const args = ["classroom", "teachers", "list", pos(courseId)];
|
|
36276
36705
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
36277
36706
|
const token = resolvePageToken({ pageToken, page });
|
|
36278
36707
|
if (token) args.push(`--page=${token}`);
|
|
@@ -36288,7 +36717,7 @@ function registerClassroomTools(server) {
|
|
|
36288
36717
|
account: accountParam
|
|
36289
36718
|
})
|
|
36290
36719
|
}, async ({ courseId, userId, account }) => {
|
|
36291
|
-
return runOrDiagnose(["classroom", "teachers", "get", courseId, userId], { account });
|
|
36720
|
+
return runOrDiagnose(["classroom", "teachers", "get", pos(courseId), pos(userId)], { account });
|
|
36292
36721
|
});
|
|
36293
36722
|
server.registerTool("gog_classroom_roster", {
|
|
36294
36723
|
description: "List the full roster (students and/or teachers) of a Google Classroom course. Omit both flags to return both groups.",
|
|
@@ -36304,7 +36733,7 @@ function registerClassroomTools(server) {
|
|
|
36304
36733
|
account: accountParam
|
|
36305
36734
|
})
|
|
36306
36735
|
}, async ({ courseId, students, teachers, max, pageToken, page, all, account }) => {
|
|
36307
|
-
const args = ["classroom", "roster", courseId];
|
|
36736
|
+
const args = ["classroom", "roster", pos(courseId)];
|
|
36308
36737
|
if (students) args.push("--students");
|
|
36309
36738
|
if (teachers) args.push("--teachers");
|
|
36310
36739
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
@@ -36329,7 +36758,7 @@ function registerClassroomTools(server) {
|
|
|
36329
36758
|
account: accountParam
|
|
36330
36759
|
})
|
|
36331
36760
|
}, async ({ courseId, state, topic, orderBy, max, pageToken, page, all, scanPages, account }) => {
|
|
36332
|
-
const args = ["classroom", "coursework", "list", courseId];
|
|
36761
|
+
const args = ["classroom", "coursework", "list", pos(courseId)];
|
|
36333
36762
|
if (state) args.push(`--state=${state}`);
|
|
36334
36763
|
if (topic) args.push(`--topic=${topic}`);
|
|
36335
36764
|
if (orderBy) args.push(`--order-by=${orderBy}`);
|
|
@@ -36349,7 +36778,7 @@ function registerClassroomTools(server) {
|
|
|
36349
36778
|
account: accountParam
|
|
36350
36779
|
})
|
|
36351
36780
|
}, async ({ courseId, courseworkId, account }) => {
|
|
36352
|
-
return runOrDiagnose(["classroom", "coursework", "get", courseId, courseworkId], { account });
|
|
36781
|
+
return runOrDiagnose(["classroom", "coursework", "get", pos(courseId), pos(courseworkId)], { account });
|
|
36353
36782
|
});
|
|
36354
36783
|
server.registerTool("gog_classroom_submissions_list", {
|
|
36355
36784
|
description: "List student submissions for a coursework item.",
|
|
@@ -36367,7 +36796,7 @@ function registerClassroomTools(server) {
|
|
|
36367
36796
|
account: accountParam
|
|
36368
36797
|
})
|
|
36369
36798
|
}, async ({ courseId, courseworkId, state, late, user, max, pageToken, page, all, account }) => {
|
|
36370
|
-
const args = ["classroom", "submissions", "list", courseId, courseworkId];
|
|
36799
|
+
const args = ["classroom", "submissions", "list", pos(courseId), pos(courseworkId)];
|
|
36371
36800
|
if (state) args.push(`--state=${state}`);
|
|
36372
36801
|
if (late) args.push(`--late=${late}`);
|
|
36373
36802
|
if (user) args.push(`--user=${user}`);
|
|
@@ -36387,7 +36816,7 @@ function registerClassroomTools(server) {
|
|
|
36387
36816
|
account: accountParam
|
|
36388
36817
|
})
|
|
36389
36818
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
36390
|
-
return runOrDiagnose(["classroom", "submissions", "get", courseId, courseworkId, submissionId], { account });
|
|
36819
|
+
return runOrDiagnose(["classroom", "submissions", "get", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
36391
36820
|
});
|
|
36392
36821
|
server.registerTool("gog_classroom_submissions_grade", {
|
|
36393
36822
|
description: "Grade a student submission. Set draft and/or assigned grade values.",
|
|
@@ -36401,7 +36830,7 @@ function registerClassroomTools(server) {
|
|
|
36401
36830
|
account: accountParam
|
|
36402
36831
|
})
|
|
36403
36832
|
}, async ({ courseId, courseworkId, submissionId, draft, assigned, account }) => {
|
|
36404
|
-
const args = ["classroom", "submissions", "grade", courseId, courseworkId, submissionId];
|
|
36833
|
+
const args = ["classroom", "submissions", "grade", pos(courseId), pos(courseworkId), pos(submissionId)];
|
|
36405
36834
|
if (draft) args.push(`--draft=${draft}`);
|
|
36406
36835
|
if (assigned) args.push(`--assigned=${assigned}`);
|
|
36407
36836
|
return runOrDiagnose(args, { account });
|
|
@@ -36416,7 +36845,7 @@ function registerClassroomTools(server) {
|
|
|
36416
36845
|
account: accountParam
|
|
36417
36846
|
})
|
|
36418
36847
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
36419
|
-
return runOrDiagnose(["classroom", "submissions", "return", courseId, courseworkId, submissionId], { account });
|
|
36848
|
+
return runOrDiagnose(["classroom", "submissions", "return", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
36420
36849
|
});
|
|
36421
36850
|
server.registerTool("gog_classroom_submissions_turn_in", {
|
|
36422
36851
|
description: "Turn in a student submission (student action).",
|
|
@@ -36428,7 +36857,7 @@ function registerClassroomTools(server) {
|
|
|
36428
36857
|
account: accountParam
|
|
36429
36858
|
})
|
|
36430
36859
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
36431
|
-
return runOrDiagnose(["classroom", "submissions", "turn-in", courseId, courseworkId, submissionId], { account });
|
|
36860
|
+
return runOrDiagnose(["classroom", "submissions", "turn-in", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
36432
36861
|
});
|
|
36433
36862
|
server.registerTool("gog_classroom_submissions_reclaim", {
|
|
36434
36863
|
description: "Reclaim a turned-in submission (student action to edit a submission).",
|
|
@@ -36440,7 +36869,7 @@ function registerClassroomTools(server) {
|
|
|
36440
36869
|
account: accountParam
|
|
36441
36870
|
})
|
|
36442
36871
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
36443
|
-
return runOrDiagnose(["classroom", "submissions", "reclaim", courseId, courseworkId, submissionId], { account });
|
|
36872
|
+
return runOrDiagnose(["classroom", "submissions", "reclaim", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
36444
36873
|
});
|
|
36445
36874
|
server.registerTool("gog_classroom_announcements_list", {
|
|
36446
36875
|
description: "List announcements in a Google Classroom course.",
|
|
@@ -36456,7 +36885,7 @@ function registerClassroomTools(server) {
|
|
|
36456
36885
|
account: accountParam
|
|
36457
36886
|
})
|
|
36458
36887
|
}, async ({ courseId, state, orderBy, max, pageToken, page, all, account }) => {
|
|
36459
|
-
const args = ["classroom", "announcements", "list", courseId];
|
|
36888
|
+
const args = ["classroom", "announcements", "list", pos(courseId)];
|
|
36460
36889
|
if (state) args.push(`--state=${state}`);
|
|
36461
36890
|
if (orderBy) args.push(`--order-by=${orderBy}`);
|
|
36462
36891
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
@@ -36474,7 +36903,7 @@ function registerClassroomTools(server) {
|
|
|
36474
36903
|
account: accountParam
|
|
36475
36904
|
})
|
|
36476
36905
|
}, async ({ courseId, announcementId, account }) => {
|
|
36477
|
-
return runOrDiagnose(["classroom", "announcements", "get", courseId, announcementId], { account });
|
|
36906
|
+
return runOrDiagnose(["classroom", "announcements", "get", pos(courseId), pos(announcementId)], { account });
|
|
36478
36907
|
});
|
|
36479
36908
|
server.registerTool("gog_classroom_announcements_create", {
|
|
36480
36909
|
description: "Create an announcement in a Google Classroom course.",
|
|
@@ -36487,7 +36916,7 @@ function registerClassroomTools(server) {
|
|
|
36487
36916
|
account: accountParam
|
|
36488
36917
|
})
|
|
36489
36918
|
}, async ({ courseId, text, state, scheduled, account }) => {
|
|
36490
|
-
const args = ["classroom", "announcements", "create", courseId, `--text=${text}`];
|
|
36919
|
+
const args = ["classroom", "announcements", "create", pos(courseId), `--text=${text}`];
|
|
36491
36920
|
if (state) args.push(`--state=${state}`);
|
|
36492
36921
|
if (scheduled) args.push(`--scheduled=${scheduled}`);
|
|
36493
36922
|
return runOrDiagnose(args, { account });
|
|
@@ -36504,7 +36933,7 @@ function registerClassroomTools(server) {
|
|
|
36504
36933
|
account: accountParam
|
|
36505
36934
|
})
|
|
36506
36935
|
}, async ({ courseId, max, pageToken, page, all, account }) => {
|
|
36507
|
-
const args = ["classroom", "topics", "list", courseId];
|
|
36936
|
+
const args = ["classroom", "topics", "list", pos(courseId)];
|
|
36508
36937
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
36509
36938
|
const token = resolvePageToken({ pageToken, page });
|
|
36510
36939
|
if (token) args.push(`--page=${token}`);
|
|
@@ -36520,7 +36949,7 @@ function registerClassroomTools(server) {
|
|
|
36520
36949
|
account: accountParam
|
|
36521
36950
|
})
|
|
36522
36951
|
}, async ({ courseId, topicId, account }) => {
|
|
36523
|
-
return runOrDiagnose(["classroom", "topics", "get", courseId, topicId], { account });
|
|
36952
|
+
return runOrDiagnose(["classroom", "topics", "get", pos(courseId), pos(topicId)], { account });
|
|
36524
36953
|
});
|
|
36525
36954
|
server.registerTool("gog_classroom_invitations_list", {
|
|
36526
36955
|
description: "List Google Classroom invitations.",
|
|
@@ -36552,7 +36981,7 @@ function registerClassroomTools(server) {
|
|
|
36552
36981
|
account: accountParam
|
|
36553
36982
|
})
|
|
36554
36983
|
}, async ({ invitationId, account }) => {
|
|
36555
|
-
return runOrDiagnose(["classroom", "invitations", "get", invitationId], { account });
|
|
36984
|
+
return runOrDiagnose(["classroom", "invitations", "get", pos(invitationId)], { account });
|
|
36556
36985
|
});
|
|
36557
36986
|
server.registerTool("gog_classroom_invitations_accept", {
|
|
36558
36987
|
description: "Accept a Google Classroom invitation.",
|
|
@@ -36562,7 +36991,7 @@ function registerClassroomTools(server) {
|
|
|
36562
36991
|
account: accountParam
|
|
36563
36992
|
})
|
|
36564
36993
|
}, async ({ invitationId, account }) => {
|
|
36565
|
-
return runOrDiagnose(["classroom", "invitations", "accept", invitationId], { account });
|
|
36994
|
+
return runOrDiagnose(["classroom", "invitations", "accept", pos(invitationId)], { account });
|
|
36566
36995
|
});
|
|
36567
36996
|
server.registerTool("gog_classroom_profile_get", {
|
|
36568
36997
|
description: "Get a Google Classroom user profile. Omit userId to fetch the authenticated user.",
|
|
@@ -36573,7 +37002,7 @@ function registerClassroomTools(server) {
|
|
|
36573
37002
|
})
|
|
36574
37003
|
}, async ({ userId, account }) => {
|
|
36575
37004
|
const args = ["classroom", "profile", "get"];
|
|
36576
|
-
if (userId) args.push(userId);
|
|
37005
|
+
if (userId) args.push(pos(userId));
|
|
36577
37006
|
return runOrDiagnose(args, { account });
|
|
36578
37007
|
});
|
|
36579
37008
|
registerRunTool(server, {
|
|
@@ -36593,7 +37022,7 @@ function registerContactsTools(server) {
|
|
|
36593
37022
|
account: accountParam
|
|
36594
37023
|
})
|
|
36595
37024
|
}, async ({ query, account }) => {
|
|
36596
|
-
return runOrDiagnose(["contacts", "search", query], { account });
|
|
37025
|
+
return runOrDiagnose(["contacts", "search", pos(query)], { account });
|
|
36597
37026
|
});
|
|
36598
37027
|
server.registerTool("gog_contacts_list", {
|
|
36599
37028
|
description: "List all Google Contacts.",
|
|
@@ -36612,7 +37041,7 @@ function registerContactsTools(server) {
|
|
|
36612
37041
|
account: accountParam
|
|
36613
37042
|
})
|
|
36614
37043
|
}, async ({ resourceName, account }) => {
|
|
36615
|
-
return runOrDiagnose(["contacts", "get", resourceName], { account });
|
|
37044
|
+
return runOrDiagnose(["contacts", "get", pos(resourceName)], { account });
|
|
36616
37045
|
});
|
|
36617
37046
|
server.registerTool("gog_contacts_create", {
|
|
36618
37047
|
description: "Create a new Google Contact.",
|
|
@@ -36648,7 +37077,7 @@ function registerDocsTools(server) {
|
|
|
36648
37077
|
account: accountParam
|
|
36649
37078
|
})
|
|
36650
37079
|
}, async ({ docId, account }) => {
|
|
36651
|
-
return runOrDiagnose(["docs", "info", docId], { account });
|
|
37080
|
+
return runOrDiagnose(["docs", "info", pos(docId)], { account });
|
|
36652
37081
|
});
|
|
36653
37082
|
server.registerTool("gog_docs_cat", {
|
|
36654
37083
|
description: "Read a Google Doc as plain text.",
|
|
@@ -36659,7 +37088,7 @@ function registerDocsTools(server) {
|
|
|
36659
37088
|
account: accountParam
|
|
36660
37089
|
})
|
|
36661
37090
|
}, async ({ docId, chips, account }) => {
|
|
36662
|
-
const args = ["docs", "cat", docId];
|
|
37091
|
+
const args = ["docs", "cat", pos(docId)];
|
|
36663
37092
|
if (chips) args.push("--chips");
|
|
36664
37093
|
return runOrDiagnose(args, { account });
|
|
36665
37094
|
});
|
|
@@ -36671,7 +37100,7 @@ function registerDocsTools(server) {
|
|
|
36671
37100
|
account: accountParam
|
|
36672
37101
|
})
|
|
36673
37102
|
}, async ({ title, account }) => {
|
|
36674
|
-
return runOrDiagnose(["docs", "create", title], { account });
|
|
37103
|
+
return runOrDiagnose(["docs", "create", pos(title)], { account });
|
|
36675
37104
|
});
|
|
36676
37105
|
server.registerTool("gog_docs_write", {
|
|
36677
37106
|
description: "Write text content to a Google Doc, replacing existing body content by default. Set append=true to add after existing content.",
|
|
@@ -36696,7 +37125,7 @@ function registerDocsTools(server) {
|
|
|
36696
37125
|
account: accountParam
|
|
36697
37126
|
})
|
|
36698
37127
|
}, async ({ docId, text, append, checkOrphans, bullets, bulletPreset, ordered, noBullets, indentStart, indentEnd, indentFirstLine, spaceAbove, spaceBelow, keepLinesTogether, keepWithNext, batch, account }) => {
|
|
36699
|
-
const args = ["docs", "write", docId, `--text=${text}`];
|
|
37128
|
+
const args = ["docs", "write", pos(docId), `--text=${text}`];
|
|
36700
37129
|
if (append) args.push("--append");
|
|
36701
37130
|
if (checkOrphans) args.push("--check-orphans");
|
|
36702
37131
|
if (bullets) args.push("--bullets");
|
|
@@ -36723,7 +37152,7 @@ function registerDocsTools(server) {
|
|
|
36723
37152
|
account: accountParam
|
|
36724
37153
|
})
|
|
36725
37154
|
}, async ({ docId, find, replace, account }) => {
|
|
36726
|
-
return runOrDiagnose(["docs", "find-replace", docId, find, replace], { account });
|
|
37155
|
+
return runOrDiagnose(["docs", "find-replace", pos(docId), pos(find), pos(replace)], { account });
|
|
36727
37156
|
});
|
|
36728
37157
|
server.registerTool("gog_docs_structure", {
|
|
36729
37158
|
description: "Show a Google Doc's structure with numbered paragraphs. Useful for understanding the document layout before making index-based edits.",
|
|
@@ -36733,7 +37162,7 @@ function registerDocsTools(server) {
|
|
|
36733
37162
|
account: accountParam
|
|
36734
37163
|
})
|
|
36735
37164
|
}, async ({ docId, account }) => {
|
|
36736
|
-
return runOrDiagnose(["docs", "structure", docId], { account });
|
|
37165
|
+
return runOrDiagnose(["docs", "structure", pos(docId)], { account });
|
|
36737
37166
|
});
|
|
36738
37167
|
registerRunTool(server, { service: "docs", examples: '"copy", "clear", "insert", "sed", "export"' });
|
|
36739
37168
|
}
|
|
@@ -36743,8 +37172,10 @@ var GOOGLE_DOC_MIME = "application/vnd.google-apps.document";
|
|
|
36743
37172
|
function fileMeta(raw) {
|
|
36744
37173
|
const parsed = JSON.parse(raw);
|
|
36745
37174
|
const f = parsed.file ?? parsed;
|
|
36746
|
-
|
|
37175
|
+
const size = f.size === void 0 ? void 0 : Number(f.size);
|
|
37176
|
+
return { name: f.name, mimeType: f.mimeType, size: Number.isFinite(size) ? size : void 0 };
|
|
36747
37177
|
}
|
|
37178
|
+
var MAX_DRIVE_READ_BYTES = MAX_INLINE_ATTACHMENT_BYTES;
|
|
36748
37179
|
var DRIVE_LS_COMPACT_FIELDS = "nextPageToken,files(id,name,mimeType,modifiedTime,size,webViewLink)";
|
|
36749
37180
|
function registerDriveTools(server) {
|
|
36750
37181
|
server.registerTool("gog_drive_ls", {
|
|
@@ -36788,7 +37219,7 @@ function registerDriveTools(server) {
|
|
|
36788
37219
|
account: accountParam
|
|
36789
37220
|
})
|
|
36790
37221
|
}, async ({ query, view, account }) => {
|
|
36791
|
-
return runOrDiagnose(["drive", "search", query], {
|
|
37222
|
+
return runOrDiagnose(["drive", "search", pos(query)], {
|
|
36792
37223
|
account,
|
|
36793
37224
|
stripMedia: resolveView(view, ["compact", "full"]) === "compact"
|
|
36794
37225
|
});
|
|
@@ -36808,7 +37239,7 @@ function registerDriveTools(server) {
|
|
|
36808
37239
|
account: accountParam
|
|
36809
37240
|
})
|
|
36810
37241
|
}, async ({ fileId, view, account }) => {
|
|
36811
|
-
return runOrDiagnose(["drive", "get", fileId], {
|
|
37242
|
+
return runOrDiagnose(["drive", "get", pos(fileId)], {
|
|
36812
37243
|
account,
|
|
36813
37244
|
stripMedia: resolveView(view, ["compact", "full"]) === "compact"
|
|
36814
37245
|
});
|
|
@@ -36821,7 +37252,7 @@ function registerDriveTools(server) {
|
|
|
36821
37252
|
account: accountParam
|
|
36822
37253
|
})
|
|
36823
37254
|
}, async ({ name, account }) => {
|
|
36824
|
-
return runOrDiagnose(["drive", "mkdir", name], { account });
|
|
37255
|
+
return runOrDiagnose(["drive", "mkdir", pos(name)], { account });
|
|
36825
37256
|
});
|
|
36826
37257
|
server.registerTool("gog_drive_rename", {
|
|
36827
37258
|
description: "Rename a file or folder in Google Drive.",
|
|
@@ -36832,7 +37263,7 @@ function registerDriveTools(server) {
|
|
|
36832
37263
|
account: accountParam
|
|
36833
37264
|
})
|
|
36834
37265
|
}, async ({ fileId, newName, account }) => {
|
|
36835
|
-
return runOrDiagnose(["drive", "rename", fileId, newName], { account });
|
|
37266
|
+
return runOrDiagnose(["drive", "rename", pos(fileId), pos(newName)], { account });
|
|
36836
37267
|
});
|
|
36837
37268
|
server.registerTool("gog_drive_move", {
|
|
36838
37269
|
description: "Move a file to a different folder in Google Drive.",
|
|
@@ -36843,7 +37274,7 @@ function registerDriveTools(server) {
|
|
|
36843
37274
|
account: accountParam
|
|
36844
37275
|
})
|
|
36845
37276
|
}, async ({ fileId, parentId, account }) => {
|
|
36846
|
-
return runOrDiagnose(["drive", "move", fileId, `--parent=${parentId}`], { account });
|
|
37277
|
+
return runOrDiagnose(["drive", "move", pos(fileId), `--parent=${parentId}`], { account });
|
|
36847
37278
|
});
|
|
36848
37279
|
server.registerTool("gog_drive_delete", {
|
|
36849
37280
|
description: "Move a Google Drive file to trash, or permanently delete it with permanent=true (irreversible).",
|
|
@@ -36854,7 +37285,7 @@ function registerDriveTools(server) {
|
|
|
36854
37285
|
account: accountParam
|
|
36855
37286
|
})
|
|
36856
37287
|
}, async ({ fileId, permanent, account }) => {
|
|
36857
|
-
const args = ["drive", "delete", fileId];
|
|
37288
|
+
const args = ["drive", "delete", pos(fileId)];
|
|
36858
37289
|
if (permanent) args.push("--permanent");
|
|
36859
37290
|
args.push("--force");
|
|
36860
37291
|
return runOrDiagnose(args, { account });
|
|
@@ -36871,7 +37302,7 @@ function registerDriveTools(server) {
|
|
|
36871
37302
|
account: accountParam
|
|
36872
37303
|
})
|
|
36873
37304
|
}, async ({ fileId, to, email: email3, domain: domain2, role, account }) => {
|
|
36874
|
-
const args = ["drive", "share", fileId, `--to=${to}`];
|
|
37305
|
+
const args = ["drive", "share", pos(fileId), `--to=${to}`];
|
|
36875
37306
|
if (email3) args.push(`--email=${email3}`);
|
|
36876
37307
|
if (domain2) args.push(`--domain=${domain2}`);
|
|
36877
37308
|
if (role) args.push(`--role=${role}`);
|
|
@@ -36894,7 +37325,7 @@ function registerDriveTools(server) {
|
|
|
36894
37325
|
}, async ({ fileId, ocrLanguage, offset = 0, maxChars, account }) => {
|
|
36895
37326
|
let tempDocId;
|
|
36896
37327
|
try {
|
|
36897
|
-
const { name, mimeType } = fileMeta(await run(["drive", "get", fileId], { account }));
|
|
37328
|
+
const { name, mimeType } = fileMeta(await run(["drive", "get", pos(fileId)], { account }));
|
|
36898
37329
|
let sourceId = fileId;
|
|
36899
37330
|
if (mimeType !== GOOGLE_DOC_MIME) {
|
|
36900
37331
|
const params = JSON.stringify({ fileId, ...ocrLanguage ? { ocrLanguage } : {} });
|
|
@@ -36939,13 +37370,13 @@ function registerDriveTools(server) {
|
|
|
36939
37370
|
return diagnose(err);
|
|
36940
37371
|
} finally {
|
|
36941
37372
|
if (tempDocId) {
|
|
36942
|
-
await run(["drive", "delete", tempDocId, "--permanent", "--force"], { account }).catch(() => {
|
|
37373
|
+
await run(["drive", "delete", pos(tempDocId), "--permanent", "--force"], { account }).catch(() => {
|
|
36943
37374
|
});
|
|
36944
37375
|
}
|
|
36945
37376
|
}
|
|
36946
37377
|
});
|
|
36947
37378
|
server.registerTool("gog_drive_read_bytes", {
|
|
36948
|
-
description: "Fetch a Drive file's raw bytes and return them base64-encoded as an embedded resource \u2014 the generic fallback for callers that want the file itself (to parse locally) rather than extracted text. For readable text from a PDF, prefer gog_drive_extract_text.",
|
|
37379
|
+
description: "Fetch a Drive file's raw bytes and return them base64-encoded as an embedded resource \u2014 the generic fallback for callers that want the file itself (to parse locally) rather than extracted text. Files over 8 MiB are refused (checked before downloading). For readable text from a PDF, prefer gog_drive_extract_text.",
|
|
36949
37380
|
annotations: { readOnlyHint: true },
|
|
36950
37381
|
inputSchema: external_exports.object({
|
|
36951
37382
|
fileId: external_exports.string().describe("Drive file ID"),
|
|
@@ -36953,9 +37384,17 @@ function registerDriveTools(server) {
|
|
|
36953
37384
|
})
|
|
36954
37385
|
}, async ({ fileId, account }) => {
|
|
36955
37386
|
try {
|
|
36956
|
-
const { name, mimeType } = fileMeta(await run(["drive", "get", fileId], { account }));
|
|
37387
|
+
const { name, mimeType, size } = fileMeta(await run(["drive", "get", pos(fileId)], { account }));
|
|
37388
|
+
if (size !== void 0 && size > MAX_DRIVE_READ_BYTES) {
|
|
37389
|
+
return errorResult(
|
|
37390
|
+
`${name ?? fileId} is ${size} bytes, over the 8 MiB this tool returns inline. Use gog_drive_extract_text for its text, or share its webViewLink (gog_drive_get) instead.`
|
|
37391
|
+
);
|
|
37392
|
+
}
|
|
36957
37393
|
const params = JSON.stringify({ fileId, alt: "media" });
|
|
36958
|
-
const blob = await runBinary(
|
|
37394
|
+
const blob = await runBinary(
|
|
37395
|
+
["api", "call", "drive", "v3", "files.get", `--params=${params}`],
|
|
37396
|
+
{ account, maxOutputBytes: MAX_DRIVE_READ_BYTES }
|
|
37397
|
+
);
|
|
36959
37398
|
const type = mimeType ?? "application/octet-stream";
|
|
36960
37399
|
const unrenderableHint = type.startsWith("image/") ? "" : ` If your client does not render a ${type} resource, gog_drive_extract_text returns the same file as text (PDFs included, via OCR).`;
|
|
36961
37400
|
return {
|
|
@@ -36975,7 +37414,7 @@ function registerDriveTools(server) {
|
|
|
36975
37414
|
return diagnose(err);
|
|
36976
37415
|
}
|
|
36977
37416
|
});
|
|
36978
|
-
registerRunTool(server, { service: "drive", examples: '"copy", "
|
|
37417
|
+
registerRunTool(server, { service: "drive", examples: '"copy", "download", "permissions"' });
|
|
36979
37418
|
}
|
|
36980
37419
|
|
|
36981
37420
|
// src/gmail-results.ts
|
|
@@ -37003,7 +37442,7 @@ async function countMatches(method, itemsKey, query, account) {
|
|
|
37003
37442
|
maxResults: COUNT_PROBE_PAGE_SIZE,
|
|
37004
37443
|
fields: `${itemsKey}/id,nextPageToken`
|
|
37005
37444
|
});
|
|
37006
|
-
const raw = await run(["api", "call", "gmail", "v1", method, `--params=${params}`], { account });
|
|
37445
|
+
const raw = await run(["api", "call", "gmail", "v1", pos(method), `--params=${params}`], { account });
|
|
37007
37446
|
const parsed = JSON.parse(raw);
|
|
37008
37447
|
const items = parsed[itemsKey];
|
|
37009
37448
|
if (!Array.isArray(items)) return {};
|
|
@@ -37042,7 +37481,7 @@ async function fetchGmailPages(runPage, itemsKey, maxPages, startToken) {
|
|
|
37042
37481
|
const result = await runPage(token);
|
|
37043
37482
|
const parsed = parsePage(result, itemsKey);
|
|
37044
37483
|
if (parsed === void 0) {
|
|
37045
|
-
return base === void 0 ? result : finish(base, itemsKey, merged, token);
|
|
37484
|
+
return base === void 0 ? result : finish(base, itemsKey, merged, token, pageErrorText(result, itemsKey, pages + 1));
|
|
37046
37485
|
}
|
|
37047
37486
|
base = parsed;
|
|
37048
37487
|
merged.push(...parsed[itemsKey]);
|
|
@@ -37073,10 +37512,18 @@ function parsePage(result, itemsKey) {
|
|
|
37073
37512
|
const obj = parsed;
|
|
37074
37513
|
return Array.isArray(obj[itemsKey]) ? obj : void 0;
|
|
37075
37514
|
}
|
|
37076
|
-
|
|
37515
|
+
var PAGE_ERROR_MAX = 2e3;
|
|
37516
|
+
function pageErrorText(result, itemsKey, n) {
|
|
37517
|
+
const first = result.content[0];
|
|
37518
|
+
const text = first?.type === "text" ? first.text.slice(0, PAGE_ERROR_MAX) : "";
|
|
37519
|
+
if (result.isError) return `page ${n} failed${text ? `: ${text}` : ""}`;
|
|
37520
|
+
return `page ${n} returned output that is not a ${itemsKey} list`;
|
|
37521
|
+
}
|
|
37522
|
+
function finish(base, itemsKey, merged, token, pageError) {
|
|
37077
37523
|
const out = { ...base, [itemsKey]: merged };
|
|
37078
37524
|
if (token === void 0) delete out.nextPageToken;
|
|
37079
37525
|
else out.nextPageToken = token;
|
|
37526
|
+
if (pageError !== void 0) out.pageError = pageError;
|
|
37080
37527
|
return rawTextResult(JSON.stringify(out));
|
|
37081
37528
|
}
|
|
37082
37529
|
|
|
@@ -37090,16 +37537,27 @@ var STAGING_TWIN = {
|
|
|
37090
37537
|
"gmail.reply-all": "gog_gmail_drafts_reply_all",
|
|
37091
37538
|
"gmail.send": "gog_gmail_drafts_create"
|
|
37092
37539
|
};
|
|
37540
|
+
var UNSUPPORTED_NOTE2 = {
|
|
37541
|
+
"gmail.drafts-send": "The draft is still saved: ask the user to review it and send it from Gmail."
|
|
37542
|
+
};
|
|
37543
|
+
var BODY_PREVIEW_MAX = 2048;
|
|
37544
|
+
function bodyPreview(text) {
|
|
37545
|
+
if (!text) return void 0;
|
|
37546
|
+
if (text.length <= BODY_PREVIEW_MAX) return text;
|
|
37547
|
+
return `${text.slice(0, BODY_PREVIEW_MAX)}\u2026 [${text.length - BODY_PREVIEW_MAX} more characters not shown]`;
|
|
37548
|
+
}
|
|
37549
|
+
function attachmentNames(paths, inline) {
|
|
37550
|
+
return [...paths ?? [], ...(inline ?? []).map((a) => a.filename)];
|
|
37551
|
+
}
|
|
37093
37552
|
function requireGmailDispatchConfirmation(ctx, op, details) {
|
|
37094
37553
|
const twin = STAGING_TWIN[op];
|
|
37554
|
+
const note = twin ? `Stage it with ${twin} instead; the user can review the draft and send it from Gmail (gog_gmail_drafts_send also asks for confirmation).` : UNSUPPORTED_NOTE2[op];
|
|
37095
37555
|
return requireConfirmation(ctx, {
|
|
37096
37556
|
action: op,
|
|
37097
|
-
message: "Review and confirm this email dispatch:",
|
|
37557
|
+
message: op === "gmail.filter-forward" ? "Review and confirm this mail-forwarding filter:" : "Review and confirm this email dispatch:",
|
|
37098
37558
|
details,
|
|
37099
|
-
confirmationLabel: "Confirm that this email should be sent now.",
|
|
37100
|
-
...
|
|
37101
|
-
unsupportedNote: `Stage it with ${twin} instead, review the draft, and send it with gog_gmail_drafts_send.`
|
|
37102
|
-
} : {}
|
|
37559
|
+
confirmationLabel: op === "gmail.filter-forward" ? "Confirm that matching mail should be forwarded automatically from now on." : "Confirm that this email should be sent now.",
|
|
37560
|
+
...note ? { unsupportedNote: note } : {}
|
|
37103
37561
|
});
|
|
37104
37562
|
}
|
|
37105
37563
|
var EMAIL_PATTERN = /[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(?:\.[a-z0-9-]+)+/gi;
|
|
@@ -37170,7 +37628,7 @@ var replySchema = {
|
|
|
37170
37628
|
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."),
|
|
37171
37629
|
subject: external_exports.string().optional().describe('Override reply subject (default: "Re: <original>"). A changed subject starts a NEW Gmail thread.'),
|
|
37172
37630
|
noQuote: external_exports.boolean().optional().describe("Do not include the original message quoted below the reply (default: the original is quoted)"),
|
|
37173
|
-
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.`),
|
|
37631
|
+
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).`),
|
|
37174
37632
|
attachInline: attachInlineParam,
|
|
37175
37633
|
from: external_exports.string().optional().describe("Send from this email address (must be a verified send-as alias)"),
|
|
37176
37634
|
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."),
|
|
@@ -37179,8 +37637,14 @@ var replySchema = {
|
|
|
37179
37637
|
signatureFile: external_exports.string().optional().describe("Append a local signature file (plain text or HTML), read on the gog server"),
|
|
37180
37638
|
account: accountParam
|
|
37181
37639
|
};
|
|
37640
|
+
function confineReplyPaths(f) {
|
|
37641
|
+
confinePaths(f.attach, "attach");
|
|
37642
|
+
if (f.bodyHtmlFile) confinePath(f.bodyHtmlFile, "bodyHtmlFile");
|
|
37643
|
+
if (f.signatureFile) confinePath(f.signatureFile, "signatureFile");
|
|
37644
|
+
}
|
|
37182
37645
|
function appendReplyFlags(args, f) {
|
|
37183
37646
|
assertNotBoth("bodyHtml", "bodyHtmlFile", f.bodyHtml, f.bodyHtmlFile);
|
|
37647
|
+
confineReplyPaths(f);
|
|
37184
37648
|
if (f.body) args.push(payloadArg("body", "body-file", f.body));
|
|
37185
37649
|
if (f.bodyHtml) args.push(payloadArg("body-html", "body-html-file", f.bodyHtml, "html"));
|
|
37186
37650
|
else if (f.bodyHtmlFile) args.push(`--body-html-file=${f.bodyHtmlFile}`);
|
|
@@ -37224,7 +37688,7 @@ function computeReplyRecipients(kind, headers, flags) {
|
|
|
37224
37688
|
return emails;
|
|
37225
37689
|
}
|
|
37226
37690
|
async function sendReply(kind, toolName, messageId, account, flags, ctx) {
|
|
37227
|
-
const metaResult = await runOrDiagnose(["gmail", "get", messageId, "--format=metadata"], { account });
|
|
37691
|
+
const metaResult = await runOrDiagnose(["gmail", "get", pos(messageId), "--format=metadata"], { account });
|
|
37228
37692
|
if (metaResult.isError) return metaResult;
|
|
37229
37693
|
const headers = parseMetadataHeaders(resultText(metaResult));
|
|
37230
37694
|
const recipients = computeReplyRecipients(kind, headers, flags);
|
|
@@ -37235,15 +37699,34 @@ async function sendReply(kind, toolName, messageId, account, flags, ctx) {
|
|
|
37235
37699
|
subject: flags.subject || (headers.subject ? `Re: ${headers.subject}` : void 0),
|
|
37236
37700
|
quoting: !flags.noQuote,
|
|
37237
37701
|
bodyLength: (flags.body ?? flags.bodyHtml ?? "").length,
|
|
37238
|
-
|
|
37702
|
+
bodyPreview: bodyPreview(flags.body ?? flags.bodyHtml),
|
|
37703
|
+
bodyHtmlFile: flags.bodyHtmlFile,
|
|
37704
|
+
attachmentCount: (flags.attach?.length ?? 0) + (flags.attachInline?.length ?? 0),
|
|
37705
|
+
attachments: attachmentNames(flags.attach, flags.attachInline)
|
|
37239
37706
|
});
|
|
37240
37707
|
if (confirmation) return confirmation;
|
|
37241
|
-
const args = ["gmail", kind, messageId];
|
|
37708
|
+
const args = ["gmail", kind, pos(messageId)];
|
|
37242
37709
|
appendReplyFlags(args, flags);
|
|
37243
37710
|
const result = await runOrDiagnose(args, { account });
|
|
37244
37711
|
if (!result.isError) logGmailDispatch(toolName, recipients, account);
|
|
37245
37712
|
return result;
|
|
37246
37713
|
}
|
|
37714
|
+
var GMAIL_RUN_BLOCKED_SETTINGS = /* @__PURE__ */ new Set(["forwarding", "autoforward", "filters", "delegates"]);
|
|
37715
|
+
function vetGmailRun(subcommand, args) {
|
|
37716
|
+
if (subcommand === "autoreply") {
|
|
37717
|
+
return "gog gmail autoreply sends mail and is not available through gog_gmail_run. Use gog_gmail_autoreply, which asks the user to confirm.";
|
|
37718
|
+
}
|
|
37719
|
+
if (GMAIL_RUN_BLOCKED_SETTINGS.has(subcommand)) {
|
|
37720
|
+
return `gog gmail ${subcommand} can forward or hand over mail and is not available through gog_gmail_run. Use the dedicated gog_gmail_* tool instead.`;
|
|
37721
|
+
}
|
|
37722
|
+
if (subcommand === "settings") {
|
|
37723
|
+
const blocked = args.find((a) => GMAIL_RUN_BLOCKED_SETTINGS.has(a.toLowerCase()));
|
|
37724
|
+
if (blocked) {
|
|
37725
|
+
return `gog gmail settings ${blocked} can forward or hand over mail and is not available through gog_gmail_run. Use the dedicated gog_gmail_* tool instead.`;
|
|
37726
|
+
}
|
|
37727
|
+
}
|
|
37728
|
+
return void 0;
|
|
37729
|
+
}
|
|
37247
37730
|
function registerGmailTools(server) {
|
|
37248
37731
|
server.registerTool("gog_gmail_search", {
|
|
37249
37732
|
description: 'Search Gmail threads using Gmail query syntax (e.g. "from:alice subject:invoice is:unread"). The query is passed verbatim to Gmail; a bare name token (from:alison) matches per Gmail\'s own heuristics, a full address (from:alison@example.com) is exact. To match a contact across several addresses, OR them: from:(a@x.com OR b@y.com). Results are ALWAYS newest-first by Gmail\'s internalDate \u2014 the wrapper sorts them, so the first result is the most recent match and a recent message can never be buried below older ones. IMPORTANT \u2014 a response carrying "truncated": true is an INCOMPLETE view of the matches: NEVER report that a message does not exist, or that there is no such mail, on the strength of one. Page through it (pass nextPageToken back as `pageToken`), set maxPages to walk several pages in one call, or narrow the query, and only then draw a conclusion. If you already know the thread, do not search for it at all \u2014 read it directly with gog_gmail_thread_get, which returns the whole thread and cannot be truncated or mis-ranked.',
|
|
@@ -37259,7 +37742,7 @@ function registerGmailTools(server) {
|
|
|
37259
37742
|
account: accountParam
|
|
37260
37743
|
})
|
|
37261
37744
|
}, async ({ query, max, pageToken, page, maxPages, all, fromContact, account }) => {
|
|
37262
|
-
const args = ["gmail", "search", query];
|
|
37745
|
+
const args = ["gmail", "search", pos(query)];
|
|
37263
37746
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
37264
37747
|
if (all) args.push("--all");
|
|
37265
37748
|
if (fromContact) args.push(`--from-contact=${fromContact}`);
|
|
@@ -37290,13 +37773,13 @@ function registerGmailTools(server) {
|
|
|
37290
37773
|
account: accountParam
|
|
37291
37774
|
})
|
|
37292
37775
|
}, async ({ messageId, format, sanitizeContent, account }) => {
|
|
37293
|
-
const args = ["gmail", "get", messageId];
|
|
37776
|
+
const args = ["gmail", "get", pos(messageId)];
|
|
37294
37777
|
if (format) args.push(`--format=${format}`);
|
|
37295
37778
|
if (sanitizeContent) args.push("--sanitize-content");
|
|
37296
37779
|
return runOrDiagnose(args, { account });
|
|
37297
37780
|
});
|
|
37298
37781
|
server.registerTool("gog_gmail_send", {
|
|
37299
|
-
description: 'SENDS MAIL \u2014 asks the MCP host to show a confirmation prompt with the recipients, subject,
|
|
37782
|
+
description: 'SENDS MAIL \u2014 asks the MCP host to show a confirmation prompt with the recipients, subject, a preview of the body and the attachment names. Mail is sent only after the user accepts that prompt. Two ways to attach a file: `attach` takes paths READ ON THE GOG SERVER, and `attachInline` takes the bytes themselves. Use attachInline unless you know the file exists on the same machine gog runs on \u2014 on the hosted connector and any remote deployment there is no shared filesystem, so no path you can name resolves there and `attach` will fail with "no such file or directory". When either is used, the JSON result echoes the attached filenames and byte sizes \u2014 check it to confirm the files were embedded. NOT the tool for answering a message: replyToMessageId only files this in the right thread \u2014 the subject, recipients and body are entirely yours, and the original is not quoted unless you set quote. Use gog_gmail_reply / gog_gmail_reply_all instead, which inherit all three.',
|
|
37300
37783
|
annotations: { destructiveHint: true },
|
|
37301
37784
|
inputSchema: external_exports.object({
|
|
37302
37785
|
to: external_exports.string().describe("Recipient(s), comma-separated"),
|
|
@@ -37307,11 +37790,12 @@ function registerGmailTools(server) {
|
|
|
37307
37790
|
replyToMessageId: external_exports.string().optional().describe('Message ID to thread this message against \u2014 sets In-Reply-To/References only. It does NOT quote the original (pass quote for that), inherit its recipients, or prefix the subject with "Re:". For an actual reply use gog_gmail_reply.'),
|
|
37308
37791
|
threadId: external_exports.string().optional().describe("Thread ID to thread this message within. Same caveat as replyToMessageId: threading only, no quote and no inherited subject or recipients."),
|
|
37309
37792
|
quote: external_exports.boolean().optional().describe("Include the original message quoted below the body. Requires replyToMessageId or threadId. gog quotes by DEFAULT on gmail reply but never on gmail send, so without this a threaded send arrives with the original nowhere in it."),
|
|
37310
|
-
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. Each file is read on the server, base64-encoded with a MIME type inferred from its extension, and added as a multipart attachment.`),
|
|
37793
|
+
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. Each file is read on the server, base64-encoded with a MIME type inferred from its extension, and added as a multipart attachment. Must be inside the server's GOG_FILE_ROOTS directories (default ~/gogcli-mcp-files).`),
|
|
37311
37794
|
attachInline: attachInlineParam,
|
|
37312
37795
|
account: accountParam
|
|
37313
37796
|
})
|
|
37314
37797
|
}, async ({ to, subject, body, cc, bcc, replyToMessageId, threadId, quote, attach, attachInline, account }, ctx) => {
|
|
37798
|
+
confinePaths(attach, "attach");
|
|
37315
37799
|
const args = ["gmail", "send", `--to=${to}`, `--subject=${subject}`, payloadArg("body", "body-file", body)];
|
|
37316
37800
|
if (cc) args.push(`--cc=${cc}`);
|
|
37317
37801
|
if (bcc) args.push(`--bcc=${bcc}`);
|
|
@@ -37330,9 +37814,11 @@ function registerGmailTools(server) {
|
|
|
37330
37814
|
recipientCount: recipients.length,
|
|
37331
37815
|
subject,
|
|
37332
37816
|
bodyLength: body.length,
|
|
37817
|
+
bodyPreview: bodyPreview(body),
|
|
37333
37818
|
threaded: Boolean(replyToMessageId || threadId),
|
|
37334
37819
|
quoting: Boolean(quote),
|
|
37335
|
-
attachmentCount: (attach?.length ?? 0) + (attachInline?.length ?? 0)
|
|
37820
|
+
attachmentCount: (attach?.length ?? 0) + (attachInline?.length ?? 0),
|
|
37821
|
+
attachments: attachmentNames(attach, attachInline)
|
|
37336
37822
|
});
|
|
37337
37823
|
if (confirmation) return confirmation;
|
|
37338
37824
|
const result = await runOrDiagnose(args, { account });
|
|
@@ -37340,22 +37826,32 @@ function registerGmailTools(server) {
|
|
|
37340
37826
|
return result;
|
|
37341
37827
|
});
|
|
37342
37828
|
server.registerTool("gog_gmail_reply", {
|
|
37343
|
-
description: 'SENDS MAIL \u2014 asks the MCP host to show a confirmation prompt with the resolved recipient, subject,
|
|
37829
|
+
description: 'SENDS MAIL \u2014 asks the MCP host to show a confirmation prompt with the resolved recipient, subject, a preview of the body and the attachment names. Mail is sent only after the user accepts that prompt. To STAGE a reply instead of sending it, use gog_gmail_drafts_reply (gogcli-mcp-gmail only), which never needs confirmation. Reply to a Gmail message (goes to the original sender only). USE THIS, not gog_gmail_send, whenever you are answering a message: it threads off the original AND inherits its "Re:" subject and quotes its body below yours, which gog_gmail_send does not \u2014 a send with replyToMessageId lands in the right thread but reads as a brand-new message, with the original nowhere in it. To answer every participant use gog_gmail_reply_all. The gogcli-mcp-gmail package adds two more routes with the same composition: gog_gmail_autoreply to reply across every message matching a query, and gog_gmail_drafts_reply to stage this exact reply as a draft instead of sending it.',
|
|
37344
37830
|
annotations: { destructiveHint: true },
|
|
37345
37831
|
inputSchema: sendReplySchema
|
|
37346
37832
|
}, async ({ messageId, account, ...flags }, ctx) => {
|
|
37347
37833
|
assertNotBoth("bodyHtml", "bodyHtmlFile", flags.bodyHtml, flags.bodyHtmlFile);
|
|
37834
|
+
confineReplyPaths(flags);
|
|
37348
37835
|
return sendReply("reply", "gog_gmail_reply", messageId, account, flags, ctx);
|
|
37349
37836
|
});
|
|
37350
37837
|
server.registerTool("gog_gmail_reply_all", {
|
|
37351
|
-
description: 'SENDS MAIL \u2014 asks the MCP host to show a confirmation prompt with the resolved recipients, subject,
|
|
37838
|
+
description: 'SENDS MAIL \u2014 asks the MCP host to show a confirmation prompt with the resolved recipients, subject, a preview of the body and the attachment names. Mail is sent only after the user accepts that prompt. To STAGE a reply-all instead of sending it, use gog_gmail_drafts_reply_all (gogcli-mcp-gmail only), which never needs confirmation. Reply to all participants of a Gmail message (the sender plus every To/Cc recipient). Same inherited "Re:" subject and quoted original as gog_gmail_reply. Use the remove flag to drop specific recipients from the reply-all.',
|
|
37352
37839
|
annotations: { destructiveHint: true },
|
|
37353
37840
|
inputSchema: sendReplySchema
|
|
37354
37841
|
}, async ({ messageId, account, ...flags }, ctx) => {
|
|
37355
37842
|
assertNotBoth("bodyHtml", "bodyHtmlFile", flags.bodyHtml, flags.bodyHtmlFile);
|
|
37843
|
+
confineReplyPaths(flags);
|
|
37356
37844
|
return sendReply("reply-all", "gog_gmail_reply_all", messageId, account, flags, ctx);
|
|
37357
37845
|
});
|
|
37358
|
-
registerRunTool(server, {
|
|
37846
|
+
registerRunTool(server, {
|
|
37847
|
+
service: "gmail",
|
|
37848
|
+
examples: '"archive", "mark-read", "labels"',
|
|
37849
|
+
// gog's --gmail-no-send blocks send/reply/forward/drafts send and all of
|
|
37850
|
+
// their aliases at runtime (verified on gog 0.41.0). Sending goes through the
|
|
37851
|
+
// confirmed tools, never this escape hatch.
|
|
37852
|
+
gmailNoSend: true,
|
|
37853
|
+
vet: vetGmailRun
|
|
37854
|
+
});
|
|
37359
37855
|
}
|
|
37360
37856
|
|
|
37361
37857
|
// src/tools/sheets-a1.ts
|
|
@@ -37424,7 +37920,7 @@ function registerSheetsTools(server) {
|
|
|
37424
37920
|
account: accountParam
|
|
37425
37921
|
})
|
|
37426
37922
|
}, async ({ spreadsheetId, range, account }) => {
|
|
37427
|
-
return runOrDiagnose(["sheets", "get", spreadsheetId, range], { account });
|
|
37923
|
+
return runOrDiagnose(["sheets", "get", pos(spreadsheetId), pos(range)], { account });
|
|
37428
37924
|
});
|
|
37429
37925
|
server.registerTool("gog_sheets_update", {
|
|
37430
37926
|
description: 'Write values to a Google Sheets range, overwriting existing content. Values may be strings, numbers, booleans, or null. Strings starting with "=" are interpreted as formulas (e.g. "=SUM(A1:A10)").',
|
|
@@ -37444,7 +37940,7 @@ function registerSheetsTools(server) {
|
|
|
37444
37940
|
const readRange = expandAnchorRange(range, values.length, cols);
|
|
37445
37941
|
let existing;
|
|
37446
37942
|
try {
|
|
37447
|
-
existing = await run(["sheets", "get", spreadsheetId, readRange], { account });
|
|
37943
|
+
existing = await run(["sheets", "get", pos(spreadsheetId), pos(readRange)], { account });
|
|
37448
37944
|
} catch (err) {
|
|
37449
37945
|
return diagnose(err);
|
|
37450
37946
|
}
|
|
@@ -37456,7 +37952,7 @@ function registerSheetsTools(server) {
|
|
|
37456
37952
|
);
|
|
37457
37953
|
}
|
|
37458
37954
|
}
|
|
37459
|
-
const args = ["sheets", "update", spreadsheetId, range, `--values-json=${JSON.stringify(values)}`];
|
|
37955
|
+
const args = ["sheets", "update", pos(spreadsheetId), pos(range), `--values-json=${JSON.stringify(values)}`];
|
|
37460
37956
|
if (dry_run) args.push("--dry-run");
|
|
37461
37957
|
if (fail_on_formula_error) args.push("--fail-on-formula-error");
|
|
37462
37958
|
return runOrDiagnose(args, { account });
|
|
@@ -37472,7 +37968,7 @@ function registerSheetsTools(server) {
|
|
|
37472
37968
|
account: accountParam
|
|
37473
37969
|
})
|
|
37474
37970
|
}, async ({ spreadsheetId, range, values, account, dry_run }) => {
|
|
37475
|
-
const args = ["sheets", "append", spreadsheetId, range, `--values-json=${JSON.stringify(values)}`];
|
|
37971
|
+
const args = ["sheets", "append", pos(spreadsheetId), pos(range), `--values-json=${JSON.stringify(values)}`];
|
|
37476
37972
|
if (dry_run) args.push("--dry-run");
|
|
37477
37973
|
return runOrDiagnose(args, { account });
|
|
37478
37974
|
});
|
|
@@ -37486,7 +37982,7 @@ function registerSheetsTools(server) {
|
|
|
37486
37982
|
account: accountParam
|
|
37487
37983
|
})
|
|
37488
37984
|
}, async ({ spreadsheetId, range, account, dry_run }) => {
|
|
37489
|
-
const args = ["sheets", "clear", spreadsheetId, range];
|
|
37985
|
+
const args = ["sheets", "clear", pos(spreadsheetId), pos(range)];
|
|
37490
37986
|
if (dry_run) args.push("--dry-run");
|
|
37491
37987
|
return runOrDiagnose(args, { account });
|
|
37492
37988
|
});
|
|
@@ -37498,7 +37994,7 @@ function registerSheetsTools(server) {
|
|
|
37498
37994
|
account: accountParam
|
|
37499
37995
|
})
|
|
37500
37996
|
}, async ({ spreadsheetId, account }) => {
|
|
37501
|
-
return runOrDiagnose(["sheets", "metadata", spreadsheetId], { account });
|
|
37997
|
+
return runOrDiagnose(["sheets", "metadata", pos(spreadsheetId)], { account });
|
|
37502
37998
|
});
|
|
37503
37999
|
server.registerTool("gog_sheets_create", {
|
|
37504
38000
|
description: "Create a new Google Spreadsheet. Returns JSON with the new spreadsheetId and URL.",
|
|
@@ -37508,7 +38004,7 @@ function registerSheetsTools(server) {
|
|
|
37508
38004
|
account: accountParam
|
|
37509
38005
|
})
|
|
37510
38006
|
}, async ({ title, account }) => {
|
|
37511
|
-
return runOrDiagnose(["sheets", "create", title], { account });
|
|
38007
|
+
return runOrDiagnose(["sheets", "create", pos(title)], { account });
|
|
37512
38008
|
});
|
|
37513
38009
|
server.registerTool("gog_sheets_find_replace", {
|
|
37514
38010
|
description: "Find and replace text across an entire Google Spreadsheet.",
|
|
@@ -37520,7 +38016,7 @@ function registerSheetsTools(server) {
|
|
|
37520
38016
|
account: accountParam
|
|
37521
38017
|
})
|
|
37522
38018
|
}, async ({ spreadsheetId, find, replace, account }) => {
|
|
37523
|
-
return runOrDiagnose(["sheets", "find-replace", spreadsheetId, find, replace], { account });
|
|
38019
|
+
return runOrDiagnose(["sheets", "find-replace", pos(spreadsheetId), pos(find), pos(replace)], { account });
|
|
37524
38020
|
});
|
|
37525
38021
|
registerRunTool(server, { service: "sheets", examples: '"freeze", "add-tab", "rename-tab"' });
|
|
37526
38022
|
}
|
|
@@ -37528,8 +38024,9 @@ function registerSheetsTools(server) {
|
|
|
37528
38024
|
// src/tools/slides.ts
|
|
37529
38025
|
function registerSlidesTools(server) {
|
|
37530
38026
|
server.registerTool("gog_slides_export", {
|
|
37531
|
-
description: "Export a Google Slides presentation to a local file (pdf or pptx).",
|
|
37532
|
-
|
|
38027
|
+
description: "Export a Google Slides presentation to a local file (pdf or pptx). The out path must be inside the server's GOG_FILE_ROOTS directories.",
|
|
38028
|
+
// Writes a file on the gog host and can overwrite one: not read-only.
|
|
38029
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
37533
38030
|
inputSchema: external_exports.object({
|
|
37534
38031
|
presentationId: external_exports.string().describe("Presentation ID"),
|
|
37535
38032
|
out: external_exports.string().optional().describe("Output file path"),
|
|
@@ -37538,7 +38035,8 @@ function registerSlidesTools(server) {
|
|
|
37538
38035
|
account: accountParam
|
|
37539
38036
|
})
|
|
37540
38037
|
}, async ({ presentationId, out, format, overwrite, account }) => {
|
|
37541
|
-
|
|
38038
|
+
if (out) confinePath(out, "out");
|
|
38039
|
+
const args = ["slides", "export", pos(presentationId)];
|
|
37542
38040
|
if (out) args.push(`--out=${out}`);
|
|
37543
38041
|
if (format) args.push(`--format=${format}`);
|
|
37544
38042
|
if (overwrite) args.push("--overwrite");
|
|
@@ -37552,7 +38050,7 @@ function registerSlidesTools(server) {
|
|
|
37552
38050
|
account: accountParam
|
|
37553
38051
|
})
|
|
37554
38052
|
}, async ({ presentationId, account }) => {
|
|
37555
|
-
return runOrDiagnose(["slides", "info", presentationId], { account });
|
|
38053
|
+
return runOrDiagnose(["slides", "info", pos(presentationId)], { account });
|
|
37556
38054
|
});
|
|
37557
38055
|
server.registerTool("gog_slides_create", {
|
|
37558
38056
|
description: "Create a new Google Slides presentation, optionally in a folder or copying from a template.",
|
|
@@ -37564,7 +38062,7 @@ function registerSlidesTools(server) {
|
|
|
37564
38062
|
account: accountParam
|
|
37565
38063
|
})
|
|
37566
38064
|
}, async ({ title, parent, template, account }) => {
|
|
37567
|
-
const args = ["slides", "create", title];
|
|
38065
|
+
const args = ["slides", "create", pos(title)];
|
|
37568
38066
|
if (parent) args.push(`--parent=${parent}`);
|
|
37569
38067
|
if (template) args.push(`--template=${template}`);
|
|
37570
38068
|
return runOrDiagnose(args, { account });
|
|
@@ -37579,7 +38077,7 @@ function registerSlidesTools(server) {
|
|
|
37579
38077
|
account: accountParam
|
|
37580
38078
|
})
|
|
37581
38079
|
}, async ({ presentationId, title, parent, account }) => {
|
|
37582
|
-
const args = ["slides", "copy", presentationId, title];
|
|
38080
|
+
const args = ["slides", "copy", pos(presentationId), pos(title)];
|
|
37583
38081
|
if (parent) args.push(`--parent=${parent}`);
|
|
37584
38082
|
return runOrDiagnose(args, { account });
|
|
37585
38083
|
});
|
|
@@ -37591,7 +38089,7 @@ function registerSlidesTools(server) {
|
|
|
37591
38089
|
account: accountParam
|
|
37592
38090
|
})
|
|
37593
38091
|
}, async ({ presentationId, account }) => {
|
|
37594
|
-
return runOrDiagnose(["slides", "list-slides", presentationId], { account });
|
|
38092
|
+
return runOrDiagnose(["slides", "list-slides", pos(presentationId)], { account });
|
|
37595
38093
|
});
|
|
37596
38094
|
server.registerTool("gog_slides_read_slide", {
|
|
37597
38095
|
description: "Read the content of a slide (text, shapes, speaker notes). Set detail=true to also include normalized element geometry, styled text runs, paragraphs, table-cell content, and image source URLs.",
|
|
@@ -37603,11 +38101,11 @@ function registerSlidesTools(server) {
|
|
|
37603
38101
|
account: accountParam
|
|
37604
38102
|
})
|
|
37605
38103
|
}, async ({ presentationId, slideId, detail, account }) => {
|
|
37606
|
-
const args = ["slides", "read-slide", presentationId, slideId];
|
|
38104
|
+
const args = ["slides", "read-slide", pos(presentationId), pos(slideId)];
|
|
37607
38105
|
if (detail) args.push("--detail");
|
|
37608
38106
|
return runOrDiagnose(args, { account });
|
|
37609
38107
|
});
|
|
37610
|
-
registerRunTool(server, { service: "slides", examples: '"
|
|
38108
|
+
registerRunTool(server, { service: "slides", examples: '"delete-slide", "update-notes"' });
|
|
37611
38109
|
}
|
|
37612
38110
|
|
|
37613
38111
|
// src/tools/tasks.ts
|
|
@@ -37629,7 +38127,7 @@ function registerTasksTools(server) {
|
|
|
37629
38127
|
account: accountParam
|
|
37630
38128
|
})
|
|
37631
38129
|
}, async ({ tasklistId, account }) => {
|
|
37632
|
-
return runOrDiagnose(["tasks", "list", tasklistId], { account });
|
|
38130
|
+
return runOrDiagnose(["tasks", "list", pos(tasklistId)], { account });
|
|
37633
38131
|
});
|
|
37634
38132
|
server.registerTool("gog_tasks_get", {
|
|
37635
38133
|
description: "Get a specific task by ID.",
|
|
@@ -37640,7 +38138,7 @@ function registerTasksTools(server) {
|
|
|
37640
38138
|
account: accountParam
|
|
37641
38139
|
})
|
|
37642
38140
|
}, async ({ tasklistId, taskId, account }) => {
|
|
37643
|
-
return runOrDiagnose(["tasks", "get", tasklistId, taskId], { account });
|
|
38141
|
+
return runOrDiagnose(["tasks", "get", pos(tasklistId), pos(taskId)], { account });
|
|
37644
38142
|
});
|
|
37645
38143
|
server.registerTool("gog_tasks_add", {
|
|
37646
38144
|
description: "Add a task to a Google Task list.",
|
|
@@ -37653,7 +38151,7 @@ function registerTasksTools(server) {
|
|
|
37653
38151
|
account: accountParam
|
|
37654
38152
|
})
|
|
37655
38153
|
}, async ({ tasklistId, title, notes, due, account }) => {
|
|
37656
|
-
const args = ["tasks", "add", tasklistId, `--title=${title}`];
|
|
38154
|
+
const args = ["tasks", "add", pos(tasklistId), `--title=${title}`];
|
|
37657
38155
|
if (notes) args.push(`--notes=${notes}`);
|
|
37658
38156
|
if (due) args.push(`--due=${due}`);
|
|
37659
38157
|
return runOrDiagnose(args, { account });
|
|
@@ -37667,7 +38165,7 @@ function registerTasksTools(server) {
|
|
|
37667
38165
|
account: accountParam
|
|
37668
38166
|
})
|
|
37669
38167
|
}, async ({ tasklistId, taskId, account }) => {
|
|
37670
|
-
return runOrDiagnose(["tasks", "done", tasklistId, taskId], { account });
|
|
38168
|
+
return runOrDiagnose(["tasks", "done", pos(tasklistId), pos(taskId)], { account });
|
|
37671
38169
|
});
|
|
37672
38170
|
server.registerTool("gog_tasks_delete", {
|
|
37673
38171
|
description: "Delete a task.",
|
|
@@ -37678,13 +38176,13 @@ function registerTasksTools(server) {
|
|
|
37678
38176
|
account: accountParam
|
|
37679
38177
|
})
|
|
37680
38178
|
}, async ({ tasklistId, taskId, account }) => {
|
|
37681
|
-
return runOrDiagnose(["tasks", "delete", tasklistId, taskId, "--force"], { account });
|
|
38179
|
+
return runOrDiagnose(["tasks", "delete", pos(tasklistId), pos(taskId), "--force"], { account });
|
|
37682
38180
|
});
|
|
37683
38181
|
registerRunTool(server, { service: "tasks", examples: '"update", "undo", "clear"' });
|
|
37684
38182
|
}
|
|
37685
38183
|
|
|
37686
38184
|
// src/server.ts
|
|
37687
|
-
var VERSION = true ? "4.
|
|
38185
|
+
var VERSION = true ? "4.3.0" : "0.0.0";
|
|
37688
38186
|
var BASE_TOOL_REGISTRARS = [
|
|
37689
38187
|
registerApiTools,
|
|
37690
38188
|
registerAppScriptTools,
|
|
@@ -37702,7 +38200,7 @@ var BASE_TOOL_REGISTRARS = [
|
|
|
37702
38200
|
];
|
|
37703
38201
|
|
|
37704
38202
|
// src/bootstrap-auth.ts
|
|
37705
|
-
import { join as
|
|
38203
|
+
import { join as join5 } from "node:path";
|
|
37706
38204
|
var AUTH_BOOTSTRAP_MARKER = "auth-bootstrap.sha256";
|
|
37707
38205
|
var VARS = ["GOG_CLIENT_ID", "GOG_CLIENT_SECRET", "GOG_REFRESH_TOKEN", "GOG_ACCOUNT"];
|
|
37708
38206
|
var log = (line) => {
|
|
@@ -37736,12 +38234,12 @@ async function bootstrapGogAuth(env = process.env, options = {}) {
|
|
|
37736
38234
|
const [clientId, clientSecret, refreshToken, account] = values;
|
|
37737
38235
|
const { spawner } = options;
|
|
37738
38236
|
try {
|
|
37739
|
-
const { createHash } = await import("node:crypto");
|
|
38237
|
+
const { createHash: createHash2 } = await import("node:crypto");
|
|
37740
38238
|
const { mkdir, readFile, writeFile, chmod } = await import("node:fs/promises");
|
|
37741
38239
|
const home = options.home ?? (await import("node:os")).homedir();
|
|
37742
|
-
const dir =
|
|
37743
|
-
const marker =
|
|
37744
|
-
const fingerprint =
|
|
38240
|
+
const dir = join5(home, ".gogcli-mcp");
|
|
38241
|
+
const marker = join5(dir, AUTH_BOOTSTRAP_MARKER);
|
|
38242
|
+
const fingerprint = createHash2("sha256").update(values.join("\0")).digest("hex");
|
|
37745
38243
|
const previous = await readFile(marker, "utf8").catch(() => void 0);
|
|
37746
38244
|
if (previous === fingerprint && await accountListed(account, spawner)) return "present";
|
|
37747
38245
|
await run(
|