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/lib.js
CHANGED
|
@@ -2001,8 +2001,8 @@ function emoji() {
|
|
|
2001
2001
|
}
|
|
2002
2002
|
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])$/;
|
|
2003
2003
|
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}|:))$/;
|
|
2004
|
-
var mac = (
|
|
2005
|
-
const escapedDelim = escapeRegex(
|
|
2004
|
+
var mac = (delimiter3) => {
|
|
2005
|
+
const escapedDelim = escapeRegex(delimiter3 ?? ":");
|
|
2006
2006
|
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
|
|
2007
2007
|
};
|
|
2008
2008
|
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])$/;
|
|
@@ -5030,7 +5030,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
|
|
|
5030
5030
|
var NONE = 0;
|
|
5031
5031
|
var ASSUMED = 1;
|
|
5032
5032
|
var PROVEN = 2;
|
|
5033
|
-
function isRecursive(inst, stack,
|
|
5033
|
+
function isRecursive(inst, stack, resolve4) {
|
|
5034
5034
|
const cached2 = recursive.get(inst);
|
|
5035
5035
|
if (cached2 !== void 0)
|
|
5036
5036
|
return cached2 ? PROVEN : NONE;
|
|
@@ -5040,7 +5040,7 @@ function isRecursive(inst, stack, resolve) {
|
|
|
5040
5040
|
let result = NONE;
|
|
5041
5041
|
const check2 = (child) => {
|
|
5042
5042
|
if (result !== PROVEN && child?._zod) {
|
|
5043
|
-
const answer = isRecursive(child, stack,
|
|
5043
|
+
const answer = isRecursive(child, stack, resolve4);
|
|
5044
5044
|
if (answer > result)
|
|
5045
5045
|
result = answer;
|
|
5046
5046
|
}
|
|
@@ -5051,7 +5051,7 @@ function isRecursive(inst, stack, resolve) {
|
|
|
5051
5051
|
const desc = Object.getOwnPropertyDescriptor(sh, key);
|
|
5052
5052
|
if (spread && !desc.enumerable)
|
|
5053
5053
|
continue;
|
|
5054
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
5054
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve4) : NONE;
|
|
5055
5055
|
if (child > answer)
|
|
5056
5056
|
answer = child;
|
|
5057
5057
|
}
|
|
@@ -5115,7 +5115,7 @@ function isRecursive(inst, stack, resolve) {
|
|
|
5115
5115
|
break;
|
|
5116
5116
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
5117
5117
|
case "lazy": {
|
|
5118
|
-
const inner = def._cachedInner ?? (
|
|
5118
|
+
const inner = def._cachedInner ?? (resolve4 ? inst._zod.innerType : void 0);
|
|
5119
5119
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
5120
5120
|
break;
|
|
5121
5121
|
}
|
|
@@ -23998,14 +23998,14 @@ function inputRequiredRoundsExceededMessage(method, maxRounds) {
|
|
|
23998
23998
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
23999
23999
|
}
|
|
24000
24000
|
function sleep(ms, signal) {
|
|
24001
|
-
return new Promise((
|
|
24001
|
+
return new Promise((resolve4, reject) => {
|
|
24002
24002
|
if (signal?.aborted) {
|
|
24003
24003
|
reject(signal.reason instanceof SdkError ? signal.reason : new SdkError(SdkErrorCode.RequestTimeout, String(signal.reason)));
|
|
24004
24004
|
return;
|
|
24005
24005
|
}
|
|
24006
24006
|
const timer = setTimeout(() => {
|
|
24007
24007
|
signal?.removeEventListener("abort", onAbort);
|
|
24008
|
-
|
|
24008
|
+
resolve4();
|
|
24009
24009
|
}, ms);
|
|
24010
24010
|
const onAbort = () => {
|
|
24011
24011
|
clearTimeout(timer);
|
|
@@ -24796,7 +24796,7 @@ var Protocol = class {
|
|
|
24796
24796
|
const flowStartedAt = Date.now();
|
|
24797
24797
|
let onAbort;
|
|
24798
24798
|
let cleanupMessageId;
|
|
24799
|
-
return new Promise((
|
|
24799
|
+
return new Promise((resolve4, reject) => {
|
|
24800
24800
|
const earlyReject = (error62) => {
|
|
24801
24801
|
reject(error62);
|
|
24802
24802
|
};
|
|
@@ -24864,7 +24864,7 @@ var Protocol = class {
|
|
|
24864
24864
|
}
|
|
24865
24865
|
if (decoded.kind === "invalid") return reject(decoded.error);
|
|
24866
24866
|
if (decoded.kind === "input_required") {
|
|
24867
|
-
if (options?.allowInputRequired === true) return
|
|
24867
|
+
if (options?.allowInputRequired === true) return resolve4(manualInputRequiredValue(decoded));
|
|
24868
24868
|
const flow = {
|
|
24869
24869
|
codec: codec2,
|
|
24870
24870
|
request,
|
|
@@ -24876,11 +24876,11 @@ var Protocol = class {
|
|
|
24876
24876
|
params
|
|
24877
24877
|
}, resultSchema, legOptions)
|
|
24878
24878
|
};
|
|
24879
|
-
return
|
|
24879
|
+
return resolve4(this._resolveNonCompleteResult(decoded, flow));
|
|
24880
24880
|
}
|
|
24881
24881
|
const result = decoded.result;
|
|
24882
24882
|
validateStandardSchema(resultSchema, result).then((parseResult) => {
|
|
24883
|
-
if (parseResult.success)
|
|
24883
|
+
if (parseResult.success) resolve4(parseResult.data);
|
|
24884
24884
|
else reject(new SdkError(SdkErrorCode.InvalidResult, `Invalid result for ${request.method}: ${parseResult.error}`));
|
|
24885
24885
|
}, reject);
|
|
24886
24886
|
});
|
|
@@ -27692,7 +27692,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
27692
27692
|
ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref);
|
|
27693
27693
|
const schOrFunc = root.refs[ref];
|
|
27694
27694
|
if (schOrFunc) return schOrFunc;
|
|
27695
|
-
let _sch =
|
|
27695
|
+
let _sch = resolve4.call(this, root, ref);
|
|
27696
27696
|
if (_sch === void 0) {
|
|
27697
27697
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
27698
27698
|
const { schemaId } = this.opts;
|
|
@@ -27718,7 +27718,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
27718
27718
|
function sameSchemaEnv(s1, s2) {
|
|
27719
27719
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
27720
27720
|
}
|
|
27721
|
-
function
|
|
27721
|
+
function resolve4(root, ref) {
|
|
27722
27722
|
let sch;
|
|
27723
27723
|
while (typeof (sch = this.refs[ref]) == "string") ref = sch;
|
|
27724
27724
|
return sch || this.schemas[ref] || resolveSchema.call(this, root, ref);
|
|
@@ -28168,47 +28168,47 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
28168
28168
|
else if (typeof uri === "object") uri = parse3(serialize(uri, options), options);
|
|
28169
28169
|
return uri;
|
|
28170
28170
|
}
|
|
28171
|
-
function
|
|
28171
|
+
function resolve4(baseURI, relativeURI, options) {
|
|
28172
28172
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
28173
28173
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
28174
28174
|
schemelessOptions.skipEscape = true;
|
|
28175
28175
|
return serialize(resolved, schemelessOptions);
|
|
28176
28176
|
}
|
|
28177
|
-
function resolveComponent(base,
|
|
28177
|
+
function resolveComponent(base, relative3, options, skipNormalization) {
|
|
28178
28178
|
const target = {};
|
|
28179
28179
|
if (!skipNormalization) {
|
|
28180
28180
|
base = parse3(serialize(base, options), options);
|
|
28181
|
-
|
|
28181
|
+
relative3 = parse3(serialize(relative3, options), options);
|
|
28182
28182
|
}
|
|
28183
28183
|
options = options || {};
|
|
28184
|
-
if (!options.tolerant &&
|
|
28185
|
-
target.scheme =
|
|
28186
|
-
target.userinfo =
|
|
28187
|
-
target.host =
|
|
28188
|
-
target.port =
|
|
28189
|
-
target.path = removeDotSegments(
|
|
28190
|
-
target.query =
|
|
28184
|
+
if (!options.tolerant && relative3.scheme) {
|
|
28185
|
+
target.scheme = relative3.scheme;
|
|
28186
|
+
target.userinfo = relative3.userinfo;
|
|
28187
|
+
target.host = relative3.host;
|
|
28188
|
+
target.port = relative3.port;
|
|
28189
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
28190
|
+
target.query = relative3.query;
|
|
28191
28191
|
} else {
|
|
28192
|
-
if (
|
|
28193
|
-
target.userinfo =
|
|
28194
|
-
target.host =
|
|
28195
|
-
target.port =
|
|
28196
|
-
target.path = removeDotSegments(
|
|
28197
|
-
target.query =
|
|
28192
|
+
if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
|
|
28193
|
+
target.userinfo = relative3.userinfo;
|
|
28194
|
+
target.host = relative3.host;
|
|
28195
|
+
target.port = relative3.port;
|
|
28196
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
28197
|
+
target.query = relative3.query;
|
|
28198
28198
|
} else {
|
|
28199
|
-
if (!
|
|
28199
|
+
if (!relative3.path) {
|
|
28200
28200
|
target.path = base.path;
|
|
28201
|
-
if (
|
|
28201
|
+
if (relative3.query !== void 0) target.query = relative3.query;
|
|
28202
28202
|
else target.query = base.query;
|
|
28203
28203
|
} else {
|
|
28204
|
-
if (
|
|
28204
|
+
if (relative3.path[0] === "/") target.path = removeDotSegments(relative3.path);
|
|
28205
28205
|
else {
|
|
28206
|
-
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) target.path = "/" +
|
|
28207
|
-
else if (!base.path) target.path =
|
|
28208
|
-
else target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
28206
|
+
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) target.path = "/" + relative3.path;
|
|
28207
|
+
else if (!base.path) target.path = relative3.path;
|
|
28208
|
+
else target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
|
|
28209
28209
|
target.path = removeDotSegments(target.path);
|
|
28210
28210
|
}
|
|
28211
|
-
target.query =
|
|
28211
|
+
target.query = relative3.query;
|
|
28212
28212
|
}
|
|
28213
28213
|
target.userinfo = base.userinfo;
|
|
28214
28214
|
target.host = base.host;
|
|
@@ -28216,7 +28216,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
28216
28216
|
}
|
|
28217
28217
|
target.scheme = base.scheme;
|
|
28218
28218
|
}
|
|
28219
|
-
target.fragment =
|
|
28219
|
+
target.fragment = relative3.fragment;
|
|
28220
28220
|
return target;
|
|
28221
28221
|
}
|
|
28222
28222
|
function equal(uriA, uriB, options) {
|
|
@@ -28342,7 +28342,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
28342
28342
|
const fastUri = {
|
|
28343
28343
|
SCHEMES,
|
|
28344
28344
|
normalize,
|
|
28345
|
-
resolve,
|
|
28345
|
+
resolve: resolve4,
|
|
28346
28346
|
resolveComponent,
|
|
28347
28347
|
equal,
|
|
28348
28348
|
serialize,
|
|
@@ -29124,14 +29124,14 @@ var require_ucs2length = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
29124
29124
|
function ucs2length(str) {
|
|
29125
29125
|
const len = str.length;
|
|
29126
29126
|
let length = 0;
|
|
29127
|
-
let
|
|
29127
|
+
let pos2 = 0;
|
|
29128
29128
|
let value;
|
|
29129
|
-
while (
|
|
29129
|
+
while (pos2 < len) {
|
|
29130
29130
|
length++;
|
|
29131
|
-
value = str.charCodeAt(
|
|
29132
|
-
if (value >= 55296 && value <= 56319 &&
|
|
29133
|
-
value = str.charCodeAt(
|
|
29134
|
-
if ((value & 64512) === 56320)
|
|
29131
|
+
value = str.charCodeAt(pos2++);
|
|
29132
|
+
if (value >= 55296 && value <= 56319 && pos2 < len) {
|
|
29133
|
+
value = str.charCodeAt(pos2);
|
|
29134
|
+
if ((value & 64512) === 56320) pos2++;
|
|
29135
29135
|
}
|
|
29136
29136
|
}
|
|
29137
29137
|
return length;
|
|
@@ -32900,13 +32900,28 @@ var API_KEY_RE = new RegExp([
|
|
|
32900
32900
|
"whsec_[A-Za-z0-9]{16,}\\b"
|
|
32901
32901
|
// webhook signing secret (Stripe-style)
|
|
32902
32902
|
].map((p) => `\\b${p}`).join("|"), "g");
|
|
32903
|
-
var QUERY_SECRET_RE = /([?&](?:
|
|
32903
|
+
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;
|
|
32904
|
+
var OAUTH_CODE_RE = /([?&]code=)(?=[^&#\s"'<>`]{16,})[^&#\s"'<>`]+/gi;
|
|
32905
|
+
var FORM_PASSWORD_RE = /((?:^|[\s,;:"'(\[{])(?:password|passwd)=)[^&#\s"'<>`]+/gim;
|
|
32906
|
+
var HEADER_SECRET_RE = /(\b(?:x[-_][a-z0-9_-]*?(?:api[-_]?key|token|secret|auth)[a-z0-9_-]*|api[-_]?key)\s*:\s*)[^\s,;"'<>`]+/gi;
|
|
32904
32907
|
var AWS_SIGV4_RE = /([?&]X-Amz-(?:Signature|Security-Token|Credential)=)[^&#\s"'<>`]+/gi;
|
|
32905
|
-
var JSON_SECRET_KEYS =
|
|
32906
|
-
|
|
32907
|
-
|
|
32908
|
+
var JSON_SECRET_KEYS = [
|
|
32909
|
+
"(?:access|refresh|id|auth|session|bearer|csrf|xsrf)[_-]?token",
|
|
32910
|
+
"client[_-]?secret",
|
|
32911
|
+
"api[_-]?secret",
|
|
32912
|
+
"(?:x[_-])?api[_-]?key",
|
|
32913
|
+
"private[_-]?key",
|
|
32914
|
+
"x[_-][a-z0-9_-]*?(?:api[_-]?key|token|secret|auth)[a-z0-9_-]*",
|
|
32915
|
+
"(?:proxy-)?authorization",
|
|
32916
|
+
"password",
|
|
32917
|
+
"passwd",
|
|
32918
|
+
"secret",
|
|
32919
|
+
"token"
|
|
32920
|
+
].join("|");
|
|
32921
|
+
var JSON_SECRET_DQ_RE = new RegExp(`("(?:${JSON_SECRET_KEYS})"\\s*:\\s*")(?:[^"\\\\]|\\\\.)*(")`, "gi");
|
|
32922
|
+
var JSON_SECRET_SQ_RE = new RegExp(`('(?:${JSON_SECRET_KEYS})'\\s*:\\s*')(?:[^'\\\\]|\\\\.)*(')`, "gi");
|
|
32908
32923
|
function redactSecrets(text) {
|
|
32909
|
-
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]");
|
|
32924
|
+
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]");
|
|
32910
32925
|
}
|
|
32911
32926
|
|
|
32912
32927
|
// ../../node_modules/@chrischall/mcp-utils/dist/response/view.js
|
|
@@ -33001,26 +33016,105 @@ function errorResult(message) {
|
|
|
33001
33016
|
}
|
|
33002
33017
|
|
|
33003
33018
|
// ../../node_modules/@chrischall/mcp-utils/dist/server/confirmation.js
|
|
33019
|
+
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
33004
33020
|
var DEFAULT_REQUEST_KEY = "confirmation";
|
|
33005
33021
|
var DEFAULT_CONFIRMATION_LABEL = "Confirm this action should proceed.";
|
|
33006
33022
|
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";
|
|
33023
|
+
var STATE_PREFIX = "mcpu.confirm.v1.";
|
|
33024
|
+
function canonicalJson(value) {
|
|
33025
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
33026
|
+
return JSON.stringify(value);
|
|
33027
|
+
if (value === void 0)
|
|
33028
|
+
return "null";
|
|
33029
|
+
if (typeof value === "number")
|
|
33030
|
+
return Number.isFinite(value) ? JSON.stringify(value) : `{"$num":"${String(value)}"}`;
|
|
33031
|
+
if (typeof value === "bigint")
|
|
33032
|
+
return `{"$bigint":"${value.toString()}"}`;
|
|
33033
|
+
if (typeof value === "function" || typeof value === "symbol") {
|
|
33034
|
+
throw new TypeError(`requireConfirmation: cannot bind a ${typeof value} argument value.`);
|
|
33035
|
+
}
|
|
33036
|
+
if (Array.isArray(value))
|
|
33037
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
33038
|
+
if (value instanceof Date) {
|
|
33039
|
+
return `{"$date":${JSON.stringify(Number.isNaN(value.getTime()) ? "Invalid Date" : value.toISOString())}}`;
|
|
33040
|
+
}
|
|
33041
|
+
if (ArrayBuffer.isView(value)) {
|
|
33042
|
+
const bytes = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
33043
|
+
return `{"$bytes":${JSON.stringify(bytes.toString("base64"))}}`;
|
|
33044
|
+
}
|
|
33045
|
+
if (value instanceof ArrayBuffer)
|
|
33046
|
+
return `{"$bytes":${JSON.stringify(Buffer.from(value).toString("base64"))}}`;
|
|
33047
|
+
if (value instanceof Map) {
|
|
33048
|
+
const entries2 = [...value.entries()].map(([k, v]) => `[${canonicalJson(k)},${canonicalJson(v)}]`).sort();
|
|
33049
|
+
return `{"$map":[${entries2.join(",")}]}`;
|
|
33050
|
+
}
|
|
33051
|
+
if (value instanceof Set) {
|
|
33052
|
+
return `{"$set":[${[...value].map(canonicalJson).sort().join(",")}]}`;
|
|
33053
|
+
}
|
|
33054
|
+
const proto = Object.getPrototypeOf(value);
|
|
33055
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
33056
|
+
throw new TypeError("requireConfirmation: cannot bind a non-plain object argument value (class instance).");
|
|
33057
|
+
}
|
|
33058
|
+
const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
33059
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(escapeKey(k))}:${canonicalJson(v)}`).join(",")}}`;
|
|
33060
|
+
}
|
|
33061
|
+
function escapeKey(key) {
|
|
33062
|
+
return key.startsWith("$") ? `$${key}` : key;
|
|
33063
|
+
}
|
|
33064
|
+
function bindingKey(binding) {
|
|
33065
|
+
if (binding.ttlSeconds !== void 0 && !(Number.isFinite(binding.ttlSeconds) && binding.ttlSeconds > 0)) {
|
|
33066
|
+
throw new RangeError("requireConfirmation: binding.ttlSeconds must be a finite number greater than 0.");
|
|
33067
|
+
}
|
|
33068
|
+
const key = typeof binding.key === "string" ? Buffer.from(binding.key, "utf8") : Buffer.from(binding.key);
|
|
33069
|
+
if (key.length < 32) {
|
|
33070
|
+
throw new RangeError("requireConfirmation: binding.key must be at least 32 bytes.");
|
|
33071
|
+
}
|
|
33072
|
+
return key;
|
|
33073
|
+
}
|
|
33074
|
+
function commitment(action, args) {
|
|
33075
|
+
return createHash("sha256").update(`${action}\0${canonicalJson(args)}`).digest("base64url");
|
|
33076
|
+
}
|
|
33077
|
+
function mintState(key, action, binding) {
|
|
33078
|
+
const exp = Math.floor(Date.now() / 1e3) + (binding.ttlSeconds ?? 600);
|
|
33079
|
+
const body = Buffer.from(JSON.stringify({ c: commitment(action, binding.args), exp })).toString("base64url");
|
|
33080
|
+
const mac3 = createHmac("sha256", key).update(`${STATE_PREFIX}${body}`).digest("base64url");
|
|
33081
|
+
return `${STATE_PREFIX}${body}.${mac3}`;
|
|
33082
|
+
}
|
|
33083
|
+
function verifyState(key, action, binding, state) {
|
|
33084
|
+
if (typeof state !== "string" || !state.startsWith(STATE_PREFIX))
|
|
33085
|
+
return false;
|
|
33086
|
+
const rest = state.slice(STATE_PREFIX.length);
|
|
33087
|
+
const dot = rest.indexOf(".");
|
|
33088
|
+
if (dot < 0)
|
|
33089
|
+
return false;
|
|
33090
|
+
const body = rest.slice(0, dot);
|
|
33091
|
+
const mac3 = Buffer.from(rest.slice(dot + 1), "base64url");
|
|
33092
|
+
const expected = createHmac("sha256", key).update(`${STATE_PREFIX}${body}`).digest();
|
|
33093
|
+
if (mac3.length !== expected.length || !timingSafeEqual(mac3, expected))
|
|
33094
|
+
return false;
|
|
33095
|
+
let payload;
|
|
33096
|
+
try {
|
|
33097
|
+
payload = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
|
|
33098
|
+
} catch {
|
|
33099
|
+
return false;
|
|
33100
|
+
}
|
|
33101
|
+
if (typeof payload.exp !== "number" || payload.exp * 1e3 <= Date.now())
|
|
33102
|
+
return false;
|
|
33103
|
+
const want = Buffer.from(commitment(action, binding.args));
|
|
33104
|
+
const got = Buffer.from(typeof payload.c === "string" ? payload.c : "");
|
|
33105
|
+
return got.length === want.length && timingSafeEqual(got, want);
|
|
33106
|
+
}
|
|
33107
|
+
function echoedState(ctx) {
|
|
33108
|
+
const accessor = ctx.mcpReq.requestState;
|
|
33109
|
+
return typeof accessor === "function" ? accessor() : void 0;
|
|
33110
|
+
}
|
|
33007
33111
|
function requireConfirmation(ctx, options) {
|
|
33008
33112
|
const requestKey = options.requestKey ?? DEFAULT_REQUEST_KEY;
|
|
33113
|
+
const key = options.binding ? bindingKey(options.binding) : void 0;
|
|
33009
33114
|
const confirmationSchema = external_exports.object({
|
|
33010
33115
|
confirmed: external_exports.boolean().describe(options.confirmationLabel ?? DEFAULT_CONFIRMATION_LABEL)
|
|
33011
33116
|
});
|
|
33012
|
-
const
|
|
33013
|
-
const accepted = acceptedContent(ctx.mcpReq.inputResponses, requestKey, confirmationSchema);
|
|
33014
|
-
if (response.kind === "missing") {
|
|
33015
|
-
if (callerAcceptsFormElicitation(ctx) === false) {
|
|
33016
|
-
return textResult({
|
|
33017
|
-
confirmed: false,
|
|
33018
|
-
dispatched: false,
|
|
33019
|
-
action: options.action,
|
|
33020
|
-
reason: "confirmation-unsupported",
|
|
33021
|
-
note: options.unsupportedNote ? `${UNSUPPORTED_NOTE}. ${options.unsupportedNote}` : `${UNSUPPORTED_NOTE}.`
|
|
33022
|
-
});
|
|
33023
|
-
}
|
|
33117
|
+
const ask = () => {
|
|
33024
33118
|
const preview = {
|
|
33025
33119
|
action: options.action,
|
|
33026
33120
|
...options.details === void 0 ? {} : { details: options.details }
|
|
@@ -33032,10 +33126,33 @@ function requireConfirmation(ctx, options) {
|
|
|
33032
33126
|
${JSON.stringify(preview, null, 2)}`,
|
|
33033
33127
|
requestedSchema: confirmationSchema
|
|
33034
33128
|
})
|
|
33035
|
-
}
|
|
33129
|
+
},
|
|
33130
|
+
...key && options.binding ? { requestState: mintState(key, options.action, options.binding) } : {}
|
|
33036
33131
|
});
|
|
33132
|
+
};
|
|
33133
|
+
const response = inputResponse(ctx.mcpReq.inputResponses, requestKey);
|
|
33134
|
+
const accepted = acceptedContent(ctx.mcpReq.inputResponses, requestKey, confirmationSchema);
|
|
33135
|
+
if (response.kind === "missing") {
|
|
33136
|
+
if (callerAcceptsFormElicitation(ctx) === false) {
|
|
33137
|
+
return textResult({
|
|
33138
|
+
confirmed: false,
|
|
33139
|
+
dispatched: false,
|
|
33140
|
+
action: options.action,
|
|
33141
|
+
reason: "confirmation-unsupported",
|
|
33142
|
+
note: options.unsupportedNote ? `${UNSUPPORTED_NOTE}. ${options.unsupportedNote}` : `${UNSUPPORTED_NOTE}.`
|
|
33143
|
+
});
|
|
33144
|
+
}
|
|
33145
|
+
return ask();
|
|
33037
33146
|
}
|
|
33038
33147
|
if (response.kind === "elicit" && response.action === "accept" && accepted?.confirmed === true) {
|
|
33148
|
+
if (key && options.binding) {
|
|
33149
|
+
const state = echoedState(ctx);
|
|
33150
|
+
if (state === void 0 || state === null) {
|
|
33151
|
+
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.`);
|
|
33152
|
+
}
|
|
33153
|
+
if (!verifyState(key, options.action, options.binding, state))
|
|
33154
|
+
return ask();
|
|
33155
|
+
}
|
|
33039
33156
|
return void 0;
|
|
33040
33157
|
}
|
|
33041
33158
|
return textResult({
|
|
@@ -33053,6 +33170,8 @@ var SERVER_PROTOCOL_VERSIONS = Object.freeze([
|
|
|
33053
33170
|
]);
|
|
33054
33171
|
|
|
33055
33172
|
// ../../node_modules/@chrischall/mcp-utils/dist/config/index.js
|
|
33173
|
+
import { homedir } from "node:os";
|
|
33174
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
33056
33175
|
var PLACEHOLDER_RE = /^\$\{[^}]*\}$/;
|
|
33057
33176
|
function readEnvVar(key, opts = {}) {
|
|
33058
33177
|
const env = opts.env ?? process.env;
|
|
@@ -33079,6 +33198,50 @@ function parseBoolEnv(key, opts = {}) {
|
|
|
33079
33198
|
return false;
|
|
33080
33199
|
return fallback;
|
|
33081
33200
|
}
|
|
33201
|
+
function expandPath(p) {
|
|
33202
|
+
let expanded = p;
|
|
33203
|
+
if (p === "~") {
|
|
33204
|
+
expanded = homedir();
|
|
33205
|
+
} else if (p.startsWith("~/")) {
|
|
33206
|
+
expanded = join(homedir(), p.slice(2));
|
|
33207
|
+
}
|
|
33208
|
+
return isAbsolute(expanded) ? expanded : resolve(expanded);
|
|
33209
|
+
}
|
|
33210
|
+
|
|
33211
|
+
// ../../node_modules/@chrischall/mcp-utils/dist/fs/confine.js
|
|
33212
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
33213
|
+
import { dirname, isAbsolute as isAbsolute2, relative, resolve as resolve2, sep } from "node:path";
|
|
33214
|
+
function isInside(root, target) {
|
|
33215
|
+
const rel = relative(root, target);
|
|
33216
|
+
return rel === "" || !rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute2(rel);
|
|
33217
|
+
}
|
|
33218
|
+
function realPathAllowingMissing(p) {
|
|
33219
|
+
const abs = resolve2(p);
|
|
33220
|
+
let existing = abs;
|
|
33221
|
+
const tail = [];
|
|
33222
|
+
while (!existsSync(existing)) {
|
|
33223
|
+
const parent = dirname(existing);
|
|
33224
|
+
if (parent === existing)
|
|
33225
|
+
break;
|
|
33226
|
+
tail.unshift(existing.slice(parent.length).replace(/^[/\\]+/, ""));
|
|
33227
|
+
existing = parent;
|
|
33228
|
+
}
|
|
33229
|
+
let real;
|
|
33230
|
+
try {
|
|
33231
|
+
real = realpathSync(existing);
|
|
33232
|
+
} catch {
|
|
33233
|
+
real = existing;
|
|
33234
|
+
}
|
|
33235
|
+
return tail.length > 0 ? resolve2(real, ...tail) : real;
|
|
33236
|
+
}
|
|
33237
|
+
function assertPathWithinRoots(path, roots) {
|
|
33238
|
+
const target = realPathAllowingMissing(expandPath(path));
|
|
33239
|
+
for (const root of roots) {
|
|
33240
|
+
if (isInside(realPathAllowingMissing(expandPath(root)), target))
|
|
33241
|
+
return target;
|
|
33242
|
+
}
|
|
33243
|
+
throw new Error(`Path is outside the allowed directories: ${path}`);
|
|
33244
|
+
}
|
|
33082
33245
|
|
|
33083
33246
|
// ../../node_modules/@chrischall/mcp-utils/dist/zod/index.js
|
|
33084
33247
|
var PositiveInt = external_exports.number().int().positive();
|
|
@@ -33100,7 +33263,7 @@ var pageSchema = {
|
|
|
33100
33263
|
};
|
|
33101
33264
|
|
|
33102
33265
|
// src/runner.ts
|
|
33103
|
-
import { delimiter, join } from "node:path";
|
|
33266
|
+
import { delimiter, join as join2 } from "node:path";
|
|
33104
33267
|
|
|
33105
33268
|
// src/timestamps.ts
|
|
33106
33269
|
var DEFAULT_DISPLAY_TZ = "America/New_York";
|
|
@@ -33301,9 +33464,48 @@ function normalizeTimestamps(text, tz = displayTimeZone(), naiveTz = naiveSource
|
|
|
33301
33464
|
return JSON.stringify(parsed, null, detectIndent(text));
|
|
33302
33465
|
}
|
|
33303
33466
|
|
|
33467
|
+
// src/arg-guard.ts
|
|
33468
|
+
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;
|
|
33469
|
+
var SHORT_ACCOUNT_CLUSTER = /^-(?!-)[A-Za-z]*a/;
|
|
33470
|
+
function forbiddenArgReason(arg) {
|
|
33471
|
+
if (arg === "--") {
|
|
33472
|
+
return 'The argument "--" is not allowed: it ends flag parsing and would disable safety flags appended after it.';
|
|
33473
|
+
}
|
|
33474
|
+
const match = FORBIDDEN_LONG_FLAG.exec(arg);
|
|
33475
|
+
if (match) {
|
|
33476
|
+
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.`;
|
|
33477
|
+
}
|
|
33478
|
+
if (SHORT_ACCOUNT_CLUSTER.test(arg)) {
|
|
33479
|
+
return `The flag ${arg} is not allowed here: -a selects the account. Use the tool's account parameter instead.`;
|
|
33480
|
+
}
|
|
33481
|
+
return void 0;
|
|
33482
|
+
}
|
|
33483
|
+
function assertSafeForwardedArgs(args) {
|
|
33484
|
+
for (const arg of args) {
|
|
33485
|
+
const reason = forbiddenArgReason(arg);
|
|
33486
|
+
if (reason) throw new Error(reason);
|
|
33487
|
+
}
|
|
33488
|
+
}
|
|
33489
|
+
var SUBCOMMAND_SHAPE = /^[a-z][a-z0-9-]*$/;
|
|
33490
|
+
function assertSafeSubcommand(subcommand) {
|
|
33491
|
+
if (!SUBCOMMAND_SHAPE.test(subcommand)) {
|
|
33492
|
+
throw new Error(
|
|
33493
|
+
`Invalid subcommand ${JSON.stringify(subcommand)}: pass a single gog subcommand name such as "list" or "mark-read", and put its arguments in args.`
|
|
33494
|
+
);
|
|
33495
|
+
}
|
|
33496
|
+
}
|
|
33497
|
+
|
|
33498
|
+
// src/argv.ts
|
|
33499
|
+
function pos(value) {
|
|
33500
|
+
return { kind: "positional", value };
|
|
33501
|
+
}
|
|
33502
|
+
function isGogPositional(arg) {
|
|
33503
|
+
return typeof arg === "object" && arg !== null && arg.kind === "positional";
|
|
33504
|
+
}
|
|
33505
|
+
|
|
33304
33506
|
// src/runner.ts
|
|
33305
33507
|
function isGogFileArg(arg) {
|
|
33306
|
-
return typeof arg !== "string";
|
|
33508
|
+
return typeof arg !== "string" && arg.kind === "file";
|
|
33307
33509
|
}
|
|
33308
33510
|
var TIMEOUT_MS = 3e4;
|
|
33309
33511
|
var MIN_GOG_VERSION = "0.41.0";
|
|
@@ -33388,8 +33590,8 @@ function formatTimeout(ms) {
|
|
|
33388
33590
|
}
|
|
33389
33591
|
async function spawnWithTempFiles(args, opts) {
|
|
33390
33592
|
const { mkdtemp, mkdir, writeFile, rm } = await import("node:fs/promises");
|
|
33391
|
-
const { tmpdir } = await import("node:os");
|
|
33392
|
-
const dir = await mkdtemp(
|
|
33593
|
+
const { tmpdir: tmpdir2 } = await import("node:os");
|
|
33594
|
+
const dir = await mkdtemp(join2(tmpdir2(), "gogcli-mcp-"));
|
|
33393
33595
|
try {
|
|
33394
33596
|
const argv = [];
|
|
33395
33597
|
let seq = 0;
|
|
@@ -33398,10 +33600,10 @@ async function spawnWithTempFiles(args, opts) {
|
|
|
33398
33600
|
argv.push(arg);
|
|
33399
33601
|
continue;
|
|
33400
33602
|
}
|
|
33401
|
-
const sub =
|
|
33603
|
+
const sub = join2(dir, String(seq));
|
|
33402
33604
|
seq += 1;
|
|
33403
33605
|
await mkdir(sub, { recursive: true, mode: 448 });
|
|
33404
|
-
const path =
|
|
33606
|
+
const path = join2(sub, arg.filename ?? `${arg.flag}.${arg.ext ?? "txt"}`);
|
|
33405
33607
|
const data = arg.encoding === "base64" ? Buffer.from(arg.contents, "base64") : Buffer.from(arg.contents, "utf8");
|
|
33406
33608
|
await writeFile(path, data, { mode: 384 });
|
|
33407
33609
|
argv.push(arg.positional ? path : `--${arg.flag}=${path}`);
|
|
@@ -33419,10 +33621,10 @@ function spawnExecutor(args, opts) {
|
|
|
33419
33621
|
return spawnGog(args, opts);
|
|
33420
33622
|
}
|
|
33421
33623
|
async function spawnGog(fullArgs, opts) {
|
|
33422
|
-
const { timeout, interactive = false, spawner, binary = false } = opts;
|
|
33624
|
+
const { timeout, interactive = false, spawner, binary = false, maxOutputBytes } = opts;
|
|
33423
33625
|
const spawn = spawner ?? (await import("node:child_process")).spawn;
|
|
33424
33626
|
const effectiveTimeout = timeout ?? TIMEOUT_MS;
|
|
33425
|
-
return new Promise((
|
|
33627
|
+
return new Promise((resolve4, reject) => {
|
|
33426
33628
|
const childEnv = { ...sanitizedEnv(), GOG_TIMEZONE: naiveSourceTimeZone(), PATH: augmentedPath() };
|
|
33427
33629
|
const child = spawn(readEnvVar("GOG_PATH") ?? "gog", fullArgs, { env: childEnv });
|
|
33428
33630
|
const stdoutChunks = [];
|
|
@@ -33440,7 +33642,17 @@ async function spawnGog(fullArgs, opts) {
|
|
|
33440
33642
|
child.kill();
|
|
33441
33643
|
reject(new Error(`gog timed out after ${formatTimeout(effectiveTimeout)}`));
|
|
33442
33644
|
}, effectiveTimeout);
|
|
33645
|
+
let stdoutBytes = 0;
|
|
33443
33646
|
child.stdout.on("data", (chunk) => {
|
|
33647
|
+
if (settled) return;
|
|
33648
|
+
stdoutBytes += chunk.length;
|
|
33649
|
+
if (maxOutputBytes !== void 0 && stdoutBytes > maxOutputBytes) {
|
|
33650
|
+
settled = true;
|
|
33651
|
+
stopWatching();
|
|
33652
|
+
child.kill();
|
|
33653
|
+
reject(new Error(`gog output exceeded the limit: more than ${maxOutputBytes} bytes`));
|
|
33654
|
+
return;
|
|
33655
|
+
}
|
|
33444
33656
|
stdoutChunks.push(chunk);
|
|
33445
33657
|
});
|
|
33446
33658
|
child.stderr.on("data", (chunk) => {
|
|
@@ -33457,14 +33669,14 @@ async function spawnGog(fullArgs, opts) {
|
|
|
33457
33669
|
const stderr = Buffer.concat(stderrChunks).toString().trim();
|
|
33458
33670
|
if (code === 0) {
|
|
33459
33671
|
if (binary) {
|
|
33460
|
-
|
|
33672
|
+
resolve4(Buffer.concat(stdoutChunks).toString("base64"));
|
|
33461
33673
|
return;
|
|
33462
33674
|
}
|
|
33463
33675
|
const stdout = Buffer.concat(stdoutChunks).toString();
|
|
33464
33676
|
if (interactive && stderr) {
|
|
33465
|
-
|
|
33677
|
+
resolve4(stdout + "\n" + stderr);
|
|
33466
33678
|
} else {
|
|
33467
|
-
|
|
33679
|
+
resolve4(stdout);
|
|
33468
33680
|
}
|
|
33469
33681
|
} else {
|
|
33470
33682
|
reject(new Error(stderr || `gog exited with code ${code}`));
|
|
@@ -33484,7 +33696,33 @@ async function spawnGog(fullArgs, opts) {
|
|
|
33484
33696
|
});
|
|
33485
33697
|
});
|
|
33486
33698
|
}
|
|
33699
|
+
function placePositionals(args) {
|
|
33700
|
+
const flags = [];
|
|
33701
|
+
const positionals = [];
|
|
33702
|
+
let afterSeparator = false;
|
|
33703
|
+
for (const arg of args) {
|
|
33704
|
+
if (arg === "--") {
|
|
33705
|
+
afterSeparator = true;
|
|
33706
|
+
} else if (isGogPositional(arg)) {
|
|
33707
|
+
positionals.push(arg.value);
|
|
33708
|
+
} else if (afterSeparator || isGogFileArg(arg) && arg.positional) {
|
|
33709
|
+
positionals.push(arg);
|
|
33710
|
+
} else {
|
|
33711
|
+
flags.push(arg);
|
|
33712
|
+
}
|
|
33713
|
+
}
|
|
33714
|
+
return { flags, positionals };
|
|
33715
|
+
}
|
|
33716
|
+
function assertNoSafetyOverrides(flags) {
|
|
33717
|
+
for (const arg of flags) {
|
|
33718
|
+
if (typeof arg !== "string") continue;
|
|
33719
|
+
const reason = forbiddenArgReason(arg);
|
|
33720
|
+
if (reason) throw new Error(reason);
|
|
33721
|
+
}
|
|
33722
|
+
}
|
|
33487
33723
|
function assembleArgs(args, opts) {
|
|
33724
|
+
const { flags, positionals } = placePositionals(args);
|
|
33725
|
+
assertNoSafetyOverrides(flags);
|
|
33488
33726
|
const effectiveAccount = opts.account ?? readEnvVar("GOG_ACCOUNT");
|
|
33489
33727
|
const fullArgs = ["--json", "--color=never"];
|
|
33490
33728
|
if (!opts.interactive) {
|
|
@@ -33493,17 +33731,21 @@ function assembleArgs(args, opts) {
|
|
|
33493
33731
|
if (opts.readonly || readonlyEnvEnabled()) {
|
|
33494
33732
|
fullArgs.push("--readonly");
|
|
33495
33733
|
}
|
|
33734
|
+
if (opts.gmailNoSend) {
|
|
33735
|
+
fullArgs.push("--gmail-no-send");
|
|
33736
|
+
}
|
|
33496
33737
|
if (effectiveAccount) {
|
|
33497
33738
|
fullArgs.push("--account", effectiveAccount);
|
|
33498
33739
|
}
|
|
33499
|
-
fullArgs.push(...
|
|
33740
|
+
fullArgs.push(...flags);
|
|
33741
|
+
if (positionals.length > 0) fullArgs.push("--", ...positionals);
|
|
33500
33742
|
return fullArgs;
|
|
33501
33743
|
}
|
|
33502
33744
|
async function run(args, options = {}) {
|
|
33503
|
-
const { account, spawner, interactive = false, timeout, readonly: readonly2 = false, redactMode = "full", opaqueFields } = options;
|
|
33745
|
+
const { account, spawner, interactive = false, timeout, readonly: readonly2 = false, redactMode = "full", opaqueFields, gmailNoSend = false } = options;
|
|
33504
33746
|
const base = redactMode === "tokens" ? redactGoogleTokens : redactSecrets2;
|
|
33505
33747
|
const redact = opaqueFields?.length ? (text) => redactPreservingOpaqueFields(text, opaqueFields, base) : base;
|
|
33506
|
-
const fullArgs = assembleArgs(args, { account, interactive, readonly: readonly2 });
|
|
33748
|
+
const fullArgs = assembleArgs(args, { account, interactive, readonly: readonly2, gmailNoSend });
|
|
33507
33749
|
try {
|
|
33508
33750
|
return redact(await spawnExecutor(fullArgs, { timeout, interactive, spawner }));
|
|
33509
33751
|
} catch (err) {
|
|
@@ -33511,9 +33753,9 @@ async function run(args, options = {}) {
|
|
|
33511
33753
|
}
|
|
33512
33754
|
}
|
|
33513
33755
|
async function runBinary(args, options = {}) {
|
|
33514
|
-
const { account, spawner, timeout, readonly: readonly2 = false } = options;
|
|
33515
|
-
const fullArgs = assembleArgs(args, { account, interactive: false, readonly: readonly2 });
|
|
33516
|
-
return spawnExecutor(fullArgs, { timeout, interactive: false, spawner, binary: true });
|
|
33756
|
+
const { account, spawner, timeout, readonly: readonly2 = false, gmailNoSend = false, maxOutputBytes } = options;
|
|
33757
|
+
const fullArgs = assembleArgs(args, { account, interactive: false, readonly: readonly2, gmailNoSend });
|
|
33758
|
+
return spawnExecutor(fullArgs, { timeout, interactive: false, spawner, binary: true, maxOutputBytes });
|
|
33517
33759
|
}
|
|
33518
33760
|
|
|
33519
33761
|
// src/pagination.ts
|
|
@@ -33565,6 +33807,175 @@ function annotateTruncatedList(result, itemsKey) {
|
|
|
33565
33807
|
return rawTextResult(JSON.stringify(out));
|
|
33566
33808
|
}
|
|
33567
33809
|
|
|
33810
|
+
// src/file-roots.ts
|
|
33811
|
+
import { homedir as homedir2 } from "node:os";
|
|
33812
|
+
import { delimiter as delimiter2, join as join4 } from "node:path";
|
|
33813
|
+
|
|
33814
|
+
// src/attachment-root.ts
|
|
33815
|
+
import { tmpdir, userInfo } from "node:os";
|
|
33816
|
+
import { dirname as dirname2, join as join3, relative as relative2, resolve as resolve3, isAbsolute as isAbsolute3, sep as sep2 } from "node:path";
|
|
33817
|
+
var ATTACHMENT_DOWNLOAD_ROOT = join3(tmpdir(), `gogcli-mcp-attachments-${userInfo().uid}`);
|
|
33818
|
+
var ATTACHMENT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
33819
|
+
function isInside2(root, target) {
|
|
33820
|
+
const rel = relative2(root, target);
|
|
33821
|
+
return rel !== "" && !rel.startsWith(`..${sep2}`) && rel !== ".." && !isAbsolute3(rel);
|
|
33822
|
+
}
|
|
33823
|
+
async function ensurePrivateDir(dir, opts = {}) {
|
|
33824
|
+
const { mkdir, lstat, chmod } = await import("node:fs/promises");
|
|
33825
|
+
try {
|
|
33826
|
+
await mkdir(dir, { mode: 448 });
|
|
33827
|
+
} catch (err) {
|
|
33828
|
+
if (err.code !== "EEXIST") throw err;
|
|
33829
|
+
}
|
|
33830
|
+
const info = await lstat(dir);
|
|
33831
|
+
if (info.isSymbolicLink() || !info.isDirectory()) {
|
|
33832
|
+
throw new Error(`refusing to use ${dir} for attachment downloads: it is not a real directory`);
|
|
33833
|
+
}
|
|
33834
|
+
const uid = opts.uid ?? userInfo().uid;
|
|
33835
|
+
if (info.uid !== uid) {
|
|
33836
|
+
throw new Error(`refusing to use ${dir} for attachment downloads: it is owned by another user`);
|
|
33837
|
+
}
|
|
33838
|
+
if ((info.mode & 63) !== 0) await chmod(dir, 448);
|
|
33839
|
+
}
|
|
33840
|
+
async function sweepExpiredDownloads(root = ATTACHMENT_DOWNLOAD_ROOT, ttlMs = ATTACHMENT_TTL_MS, now = Date.now()) {
|
|
33841
|
+
const { readdir, lstat, rm } = await import("node:fs/promises");
|
|
33842
|
+
let entries;
|
|
33843
|
+
try {
|
|
33844
|
+
entries = await readdir(root);
|
|
33845
|
+
} catch {
|
|
33846
|
+
return 0;
|
|
33847
|
+
}
|
|
33848
|
+
let removed = 0;
|
|
33849
|
+
for (const name of entries) {
|
|
33850
|
+
const path = join3(root, name);
|
|
33851
|
+
try {
|
|
33852
|
+
const info = await lstat(path);
|
|
33853
|
+
if (now - info.mtimeMs > ttlMs) {
|
|
33854
|
+
await rm(path, { recursive: true, force: true });
|
|
33855
|
+
removed += 1;
|
|
33856
|
+
}
|
|
33857
|
+
} catch {
|
|
33858
|
+
}
|
|
33859
|
+
}
|
|
33860
|
+
return removed;
|
|
33861
|
+
}
|
|
33862
|
+
async function removeDownload(path, root = ATTACHMENT_DOWNLOAD_ROOT) {
|
|
33863
|
+
const { rm, rmdir } = await import("node:fs/promises");
|
|
33864
|
+
const base = resolve3(root);
|
|
33865
|
+
const target = resolve3(base, path);
|
|
33866
|
+
if (!isInside2(base, target)) return;
|
|
33867
|
+
await rm(target, { force: true });
|
|
33868
|
+
for (let dir = dirname2(target); isInside2(base, dir); dir = dirname2(dir)) {
|
|
33869
|
+
try {
|
|
33870
|
+
await rmdir(dir);
|
|
33871
|
+
} catch {
|
|
33872
|
+
break;
|
|
33873
|
+
}
|
|
33874
|
+
}
|
|
33875
|
+
}
|
|
33876
|
+
async function prepareDownloadRoot(root = ATTACHMENT_DOWNLOAD_ROOT) {
|
|
33877
|
+
await ensurePrivateDir(root);
|
|
33878
|
+
await sweepExpiredDownloads(root);
|
|
33879
|
+
}
|
|
33880
|
+
|
|
33881
|
+
// src/file-roots.ts
|
|
33882
|
+
var FILE_ROOTS_ENV = "GOG_FILE_ROOTS";
|
|
33883
|
+
function defaultFileRoot() {
|
|
33884
|
+
return join4(homedir2(), "gogcli-mcp-files");
|
|
33885
|
+
}
|
|
33886
|
+
function fileRoots() {
|
|
33887
|
+
const raw = readEnvVar(FILE_ROOTS_ENV);
|
|
33888
|
+
const roots = (raw ?? "").split(delimiter2).map((r) => r.trim()).filter(Boolean);
|
|
33889
|
+
return roots.length > 0 ? roots : [defaultFileRoot()];
|
|
33890
|
+
}
|
|
33891
|
+
function confinePath(path, param, opts = {}) {
|
|
33892
|
+
if (opts.allowDash && path === "-") return path;
|
|
33893
|
+
const roots = [...fileRoots(), ATTACHMENT_DOWNLOAD_ROOT];
|
|
33894
|
+
try {
|
|
33895
|
+
assertPathWithinRoots(path, roots);
|
|
33896
|
+
} catch {
|
|
33897
|
+
throw new Error(
|
|
33898
|
+
`${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}.`
|
|
33899
|
+
);
|
|
33900
|
+
}
|
|
33901
|
+
return path;
|
|
33902
|
+
}
|
|
33903
|
+
function confinePaths(paths, param) {
|
|
33904
|
+
for (const path of paths ?? []) confinePath(path, param);
|
|
33905
|
+
}
|
|
33906
|
+
function confineAtFile(value, param) {
|
|
33907
|
+
if (value.startsWith("@")) confinePath(value.slice(1), param);
|
|
33908
|
+
return value;
|
|
33909
|
+
}
|
|
33910
|
+
|
|
33911
|
+
// src/run-path-guard.ts
|
|
33912
|
+
var POSITIONAL_PATH_SUBCOMMANDS = {
|
|
33913
|
+
appscript: { pull: "gog_appscript_pull" },
|
|
33914
|
+
drive: { upload: "gog_drive_upload", sync: "gog_drive_sync_push" },
|
|
33915
|
+
gmail: { import: "gog_gmail_import" },
|
|
33916
|
+
slides: {
|
|
33917
|
+
"add-slide": "gog_slides_add_slide",
|
|
33918
|
+
"insert-image": "gog_slides_insert_image",
|
|
33919
|
+
"replace-slide": "gog_slides_replace_slide"
|
|
33920
|
+
}
|
|
33921
|
+
};
|
|
33922
|
+
var PATH_FLAGS = /* @__PURE__ */ new Set(["out", "output", "out-dir", "output-dir", "dir", "worker-dir", "attach", "file", "key", "cert", "replacements"]);
|
|
33923
|
+
var ID_FLAGS = {
|
|
33924
|
+
drive: /* @__PURE__ */ new Set(["file", "filter-file"])
|
|
33925
|
+
};
|
|
33926
|
+
var EXEC_FLAGS = /* @__PURE__ */ new Set(["on-change", "on-new", "mmdc"]);
|
|
33927
|
+
var SHORT_PATH_CLUSTER = /^-(?!-)[A-Za-z]*[of]/;
|
|
33928
|
+
var LONG_FLAG = /^--([A-Za-z0-9][A-Za-z0-9-]*)(?:=([\s\S]*))?$/;
|
|
33929
|
+
function isPathFlag(service, name) {
|
|
33930
|
+
if (ID_FLAGS[service]?.has(name)) return false;
|
|
33931
|
+
return PATH_FLAGS.has(name) || name.endsWith("-file");
|
|
33932
|
+
}
|
|
33933
|
+
function confineFlagValue(flag, value) {
|
|
33934
|
+
if (value === "-") return;
|
|
33935
|
+
confinePath(value, flag);
|
|
33936
|
+
for (const part of value.split(",")) {
|
|
33937
|
+
if (part !== "" && part !== "-") confinePath(part, flag);
|
|
33938
|
+
}
|
|
33939
|
+
}
|
|
33940
|
+
function positionalPathTool(service, subcommand) {
|
|
33941
|
+
return POSITIONAL_PATH_SUBCOMMANDS[service]?.[subcommand];
|
|
33942
|
+
}
|
|
33943
|
+
function assertRunPathsConfined(service, subcommand, args) {
|
|
33944
|
+
const dedicated = positionalPathTool(service, subcommand);
|
|
33945
|
+
if (dedicated) {
|
|
33946
|
+
throw new Error(
|
|
33947
|
+
`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.`
|
|
33948
|
+
);
|
|
33949
|
+
}
|
|
33950
|
+
for (let i = 0; i < args.length; i++) {
|
|
33951
|
+
const arg = args[i];
|
|
33952
|
+
if (SHORT_PATH_CLUSTER.test(arg)) {
|
|
33953
|
+
throw new Error(
|
|
33954
|
+
`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.`
|
|
33955
|
+
);
|
|
33956
|
+
}
|
|
33957
|
+
const match = LONG_FLAG.exec(arg);
|
|
33958
|
+
if (!match) continue;
|
|
33959
|
+
const name = match[1].toLowerCase();
|
|
33960
|
+
if (EXEC_FLAGS.has(name)) {
|
|
33961
|
+
throw new Error(`The flag --${name} is not allowed here: it runs a local program chosen by the caller.`);
|
|
33962
|
+
}
|
|
33963
|
+
const isJson = name.endsWith("-json");
|
|
33964
|
+
if (!isJson && !isPathFlag(service, name)) continue;
|
|
33965
|
+
let value = match[2];
|
|
33966
|
+
if (value === void 0) {
|
|
33967
|
+
value = args[i + 1];
|
|
33968
|
+
if (value === void 0) continue;
|
|
33969
|
+
i++;
|
|
33970
|
+
}
|
|
33971
|
+
if (isJson) {
|
|
33972
|
+
if (value.startsWith("@")) confineFlagValue(`--${name}`, value.slice(1));
|
|
33973
|
+
continue;
|
|
33974
|
+
}
|
|
33975
|
+
confineFlagValue(`--${name}`, value);
|
|
33976
|
+
}
|
|
33977
|
+
}
|
|
33978
|
+
|
|
33568
33979
|
// src/tools/utils.ts
|
|
33569
33980
|
var PAYLOAD_INLINE_MAX = 4096;
|
|
33570
33981
|
function payloadArg(inlineFlag, fileFlag, value, ext) {
|
|
@@ -33622,9 +34033,17 @@ function pushPaginationFlags(args, p) {
|
|
|
33622
34033
|
if (p.all) args.push("--all");
|
|
33623
34034
|
}
|
|
33624
34035
|
function registerRunTool(server, options) {
|
|
33625
|
-
const { service, examples, omitAccount = false, note } = options;
|
|
34036
|
+
const { service, examples, omitAccount = false, note, allowedSubcommands, vet, gmailNoSend = false } = options;
|
|
34037
|
+
for (const [, example] of examples.matchAll(/"([^"]+)"/g)) {
|
|
34038
|
+
const refused = allowedSubcommands && !allowedSubcommands.includes(example) || vet?.(example, []) || positionalPathTool(service, example);
|
|
34039
|
+
if (refused) {
|
|
34040
|
+
throw new Error(`gog_${service}_run example "${example}" is refused by the tool itself; list a subcommand it runs.`);
|
|
34041
|
+
}
|
|
34042
|
+
}
|
|
33626
34043
|
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.`;
|
|
33627
|
-
const
|
|
34044
|
+
const restriction = allowedSubcommands ? ` Only these subcommands are available: ${allowedSubcommands.join(", ")}.` : "";
|
|
34045
|
+
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.';
|
|
34046
|
+
const description = `${baseDescription}${restriction}${safety}${note ? ` ${note}` : ""}`;
|
|
33628
34047
|
const inputSchema = {
|
|
33629
34048
|
subcommand: external_exports.string().describe(`The gog ${service} subcommand to run, e.g. ${examples}`),
|
|
33630
34049
|
args: external_exports.array(external_exports.string()).describe("Additional positional args and flags")
|
|
@@ -33638,7 +34057,21 @@ function registerRunTool(server, options) {
|
|
|
33638
34057
|
inputSchema: external_exports.object(inputSchema)
|
|
33639
34058
|
}, async (rawArgs) => {
|
|
33640
34059
|
const { subcommand, args, account } = rawArgs;
|
|
33641
|
-
|
|
34060
|
+
try {
|
|
34061
|
+
assertSafeSubcommand(subcommand);
|
|
34062
|
+
assertSafeForwardedArgs(args);
|
|
34063
|
+
if (allowedSubcommands && !allowedSubcommands.includes(subcommand)) {
|
|
34064
|
+
throw new Error(
|
|
34065
|
+
`gog ${service} ${subcommand} is not available through gog_${service}_run. Allowed: ${allowedSubcommands.join(", ")}.`
|
|
34066
|
+
);
|
|
34067
|
+
}
|
|
34068
|
+
const refusal = vet?.(subcommand, args);
|
|
34069
|
+
if (refusal) throw new Error(refusal);
|
|
34070
|
+
assertRunPathsConfined(service, subcommand, args);
|
|
34071
|
+
} catch (err) {
|
|
34072
|
+
return errorResult(errorText(err));
|
|
34073
|
+
}
|
|
34074
|
+
return runOrDiagnose([service, subcommand, ...args], gmailNoSend ? { account, gmailNoSend } : { account });
|
|
33642
34075
|
});
|
|
33643
34076
|
}
|
|
33644
34077
|
function errorText(err) {
|
|
@@ -33767,6 +34200,13 @@ function assertNotBoth(inlineParam, fileParam, inlineValue, fileValue) {
|
|
|
33767
34200
|
}
|
|
33768
34201
|
|
|
33769
34202
|
// src/tools/api.ts
|
|
34203
|
+
var GMAIL_API_BLOCKED = /(?:\.send$|forwarding|filters\.create|filters\.update|delegates\.create)/i;
|
|
34204
|
+
function refusedApiCall(api, method) {
|
|
34205
|
+
if (api.trim().toLowerCase() === "gmail" && GMAIL_API_BLOCKED.test(method.trim())) {
|
|
34206
|
+
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.`;
|
|
34207
|
+
}
|
|
34208
|
+
return void 0;
|
|
34209
|
+
}
|
|
33770
34210
|
function registerApiTools(server) {
|
|
33771
34211
|
server.registerTool("gog_api_list", {
|
|
33772
34212
|
description: "List the Google Discovery APIs available for gog_api_call / gog_api_describe (name + version + title).",
|
|
@@ -33790,12 +34230,12 @@ function registerApiTools(server) {
|
|
|
33790
34230
|
account: accountParam
|
|
33791
34231
|
})
|
|
33792
34232
|
}, async ({ api, version: version2, method, account }) => {
|
|
33793
|
-
const args = ["api", "describe", api, version2];
|
|
33794
|
-
if (method) args.push(method);
|
|
34233
|
+
const args = ["api", "describe", pos(api), pos(version2)];
|
|
34234
|
+
if (method) args.push(pos(method));
|
|
33795
34235
|
return runOrDiagnose(args, { account });
|
|
33796
34236
|
});
|
|
33797
34237
|
server.registerTool("gog_api_call", {
|
|
33798
|
-
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.",
|
|
34238
|
+
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.",
|
|
33799
34239
|
annotations: { destructiveHint: true },
|
|
33800
34240
|
inputSchema: external_exports.object({
|
|
33801
34241
|
api: external_exports.string().describe("Discovery API name (e.g. drive, gmail, calendar)"),
|
|
@@ -33809,14 +34249,23 @@ function registerApiTools(server) {
|
|
|
33809
34249
|
account: accountParam
|
|
33810
34250
|
})
|
|
33811
34251
|
}, async ({ api, version: version2, method, params, body, scope, allowWrite, dryRun, account }) => {
|
|
33812
|
-
|
|
34252
|
+
try {
|
|
34253
|
+
assertSafeForwardedArgs([api, version2, method]);
|
|
34254
|
+
const refusal = refusedApiCall(api, method);
|
|
34255
|
+
if (refusal) throw new Error(refusal);
|
|
34256
|
+
if (body) confineAtFile(body, "body");
|
|
34257
|
+
if (params) confineAtFile(params, "params");
|
|
34258
|
+
} catch (err) {
|
|
34259
|
+
return errorResult(errorText(err));
|
|
34260
|
+
}
|
|
34261
|
+
const args = ["api", "call", pos(api), pos(version2), pos(method)];
|
|
33813
34262
|
if (params) args.push(`--params=${params}`);
|
|
33814
34263
|
if (body) args.push(`--body=${body}`);
|
|
33815
34264
|
if (scope) args.push(`--scope=${scope}`);
|
|
33816
34265
|
if (allowWrite) args.push("--allow-write");
|
|
33817
34266
|
if (dryRun) args.push("--dry-run");
|
|
33818
34267
|
if (allowWrite) args.push("--force");
|
|
33819
|
-
return runOrDiagnose(args, { account });
|
|
34268
|
+
return runOrDiagnose(args, { account, gmailNoSend: true });
|
|
33820
34269
|
});
|
|
33821
34270
|
}
|
|
33822
34271
|
|
|
@@ -33834,7 +34283,7 @@ function registerAppScriptTools(server) {
|
|
|
33834
34283
|
account: accountParam
|
|
33835
34284
|
})
|
|
33836
34285
|
}, async ({ scriptId, account }) => {
|
|
33837
|
-
return runOrDiagnose(["appscript", "get", scriptId], { account });
|
|
34286
|
+
return runOrDiagnose(["appscript", "get", pos(scriptId)], { account });
|
|
33838
34287
|
});
|
|
33839
34288
|
server.registerTool("gog_appscript_content", {
|
|
33840
34289
|
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,
|
|
@@ -33844,19 +34293,20 @@ function registerAppScriptTools(server) {
|
|
|
33844
34293
|
account: accountParam
|
|
33845
34294
|
})
|
|
33846
34295
|
}, async ({ scriptId, account }) => {
|
|
33847
|
-
return runOrDiagnose(["appscript", "content", scriptId], { account });
|
|
34296
|
+
return runOrDiagnose(["appscript", "content", pos(scriptId)], { account });
|
|
33848
34297
|
});
|
|
33849
34298
|
server.registerTool("gog_appscript_pull", {
|
|
33850
34299
|
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,
|
|
33851
34300
|
annotations: { destructiveHint: true },
|
|
33852
34301
|
inputSchema: external_exports.object({
|
|
33853
34302
|
scriptId: scriptIdParam,
|
|
33854
|
-
dir: external_exports.string().describe("Destination directory, resolved on the machine where gog runs"),
|
|
34303
|
+
dir: external_exports.string().describe("Destination directory, resolved on the machine where gog runs; must be inside the server's GOG_FILE_ROOTS directories"),
|
|
33855
34304
|
overwrite: external_exports.boolean().optional().describe("Overwrite files that already exist in dir"),
|
|
33856
34305
|
account: accountParam
|
|
33857
34306
|
})
|
|
33858
34307
|
}, async ({ scriptId, dir, overwrite, account }) => {
|
|
33859
|
-
|
|
34308
|
+
confinePath(dir, "dir");
|
|
34309
|
+
const args = ["appscript", "pull", pos(scriptId), pos(dir)];
|
|
33860
34310
|
if (overwrite) args.push("--overwrite");
|
|
33861
34311
|
return runOrDiagnose(args, { account });
|
|
33862
34312
|
});
|
|
@@ -33882,7 +34332,7 @@ function registerAppScriptTools(server) {
|
|
|
33882
34332
|
account: accountParam
|
|
33883
34333
|
})
|
|
33884
34334
|
}, async ({ scriptId, max, pageToken, page, all, account }) => {
|
|
33885
|
-
const args = ["appscript", "deployments", scriptId];
|
|
34335
|
+
const args = ["appscript", "deployments", pos(scriptId)];
|
|
33886
34336
|
pushPaginationFlags(args, { max, pageToken, page, all });
|
|
33887
34337
|
return runOrDiagnose(args, { account });
|
|
33888
34338
|
});
|
|
@@ -33895,7 +34345,7 @@ function registerAppScriptTools(server) {
|
|
|
33895
34345
|
account: accountParam
|
|
33896
34346
|
})
|
|
33897
34347
|
}, async ({ scriptId, max, pageToken, page, all, account }) => {
|
|
33898
|
-
const args = ["appscript", "versions", scriptId];
|
|
34348
|
+
const args = ["appscript", "versions", pos(scriptId)];
|
|
33899
34349
|
pushPaginationFlags(args, { max, pageToken, page, all });
|
|
33900
34350
|
return runOrDiagnose(args, { account });
|
|
33901
34351
|
});
|
|
@@ -33921,7 +34371,7 @@ function registerAppScriptTools(server) {
|
|
|
33921
34371
|
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}`);
|
|
33922
34372
|
}
|
|
33923
34373
|
}
|
|
33924
|
-
const args = ["appscript", "run", scriptId, functionName];
|
|
34374
|
+
const args = ["appscript", "run", pos(scriptId), pos(functionName)];
|
|
33925
34375
|
if (params !== void 0) args.push(`--params=${params}`);
|
|
33926
34376
|
if (devMode) args.push("--dev-mode");
|
|
33927
34377
|
return runOrDiagnose(args, { account });
|
|
@@ -33934,6 +34384,7 @@ function registerAppScriptTools(server) {
|
|
|
33934
34384
|
}
|
|
33935
34385
|
|
|
33936
34386
|
// src/tools/auth.ts
|
|
34387
|
+
var AUTH_RUN_SUBCOMMANDS = ["list", "status", "services", "remove", "alias"];
|
|
33937
34388
|
function registerAuthToolsWith(server, defaultServices) {
|
|
33938
34389
|
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.`;
|
|
33939
34390
|
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.";
|
|
@@ -33991,7 +34442,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
33991
34442
|
})
|
|
33992
34443
|
}, async ({ email: email3, services = defaultServices, extraScopes }) => {
|
|
33993
34444
|
try {
|
|
33994
|
-
const args = ["auth", "add", email3, "--services", services];
|
|
34445
|
+
const args = ["auth", "add", pos(email3), "--services", services];
|
|
33995
34446
|
if (extraScopes) args.push(`--extra-scopes=${extraScopes}`, "--force-consent");
|
|
33996
34447
|
return rawTextResult(await run(args, {
|
|
33997
34448
|
interactive: true,
|
|
@@ -34011,7 +34462,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
34011
34462
|
})
|
|
34012
34463
|
}, async ({ email: email3, services = defaultServices, extraScopes }) => {
|
|
34013
34464
|
try {
|
|
34014
|
-
const args = ["auth", "add", email3, "--remote", "--step", "1", "--services", services, "--force-consent"];
|
|
34465
|
+
const args = ["auth", "add", pos(email3), "--remote", "--step", "1", "--services", services, "--force-consent"];
|
|
34015
34466
|
if (extraScopes) args.push(`--extra-scopes=${extraScopes}`);
|
|
34016
34467
|
return rawTextResult(await run(args, { redactMode: "tokens" }));
|
|
34017
34468
|
} catch (err) {
|
|
@@ -34038,7 +34489,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
34038
34489
|
const args = [
|
|
34039
34490
|
"auth",
|
|
34040
34491
|
"add",
|
|
34041
|
-
email3,
|
|
34492
|
+
pos(email3),
|
|
34042
34493
|
"--remote",
|
|
34043
34494
|
"--step",
|
|
34044
34495
|
"2",
|
|
@@ -34058,6 +34509,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
34058
34509
|
service: "auth",
|
|
34059
34510
|
examples: '"remove", "alias", "list"',
|
|
34060
34511
|
omitAccount: true,
|
|
34512
|
+
allowedSubcommands: AUTH_RUN_SUBCOMMANDS,
|
|
34061
34513
|
note: "For browser-based authorization, use gog_auth_add instead."
|
|
34062
34514
|
});
|
|
34063
34515
|
}
|
|
@@ -34122,7 +34574,7 @@ function registerCalendarTools(server) {
|
|
|
34122
34574
|
})
|
|
34123
34575
|
}, async ({ calendarId, from, to, days, today, query, max, pageToken, page, all, eventTypes, timezone, view, account }) => {
|
|
34124
34576
|
const args = ["calendar", "events"];
|
|
34125
|
-
if (calendarId) args.push(calendarId);
|
|
34577
|
+
if (calendarId) args.push(pos(calendarId));
|
|
34126
34578
|
if (from) args.push(`--from=${from}`);
|
|
34127
34579
|
if (to) args.push(`--to=${to}`);
|
|
34128
34580
|
if (days !== void 0) args.push(`--days=${days}`);
|
|
@@ -34151,7 +34603,7 @@ function registerCalendarTools(server) {
|
|
|
34151
34603
|
account: accountParam
|
|
34152
34604
|
})
|
|
34153
34605
|
}, async ({ calendarId, eventId, timezone, account }) => {
|
|
34154
|
-
const args = ["calendar", "event", calendarId, eventId];
|
|
34606
|
+
const args = ["calendar", "event", pos(calendarId), pos(eventId)];
|
|
34155
34607
|
if (timezone) args.push(`--timezone=${timezone}`);
|
|
34156
34608
|
return runOrDiagnose(args, { account });
|
|
34157
34609
|
});
|
|
@@ -34173,7 +34625,7 @@ function registerCalendarTools(server) {
|
|
|
34173
34625
|
account: accountParam
|
|
34174
34626
|
})
|
|
34175
34627
|
}, async ({ calendarId, summary, from, to, description, location, attendees, allDay, timezone, withZoom, reminders, noReminders, account }) => {
|
|
34176
|
-
const args = ["calendar", "create", calendarId, `--summary=${summary}`, `--from=${from}`, `--to=${to}`];
|
|
34628
|
+
const args = ["calendar", "create", pos(calendarId), `--summary=${summary}`, `--from=${from}`, `--to=${to}`];
|
|
34177
34629
|
if (description) args.push(`--description=${description}`);
|
|
34178
34630
|
if (location) args.push(`--location=${location}`);
|
|
34179
34631
|
if (attendees) args.push(`--attendees=${attendees}`);
|
|
@@ -34205,7 +34657,7 @@ function registerCalendarTools(server) {
|
|
|
34205
34657
|
account: accountParam
|
|
34206
34658
|
})
|
|
34207
34659
|
}, async ({ calendarId, eventId, summary, from, to, description, location, attendees, addAttendees, attachments, withZoom, regenerateZoom, removeZoom, removeMeet, reminders, noReminders, account }) => {
|
|
34208
|
-
const args = ["calendar", "update", calendarId, eventId];
|
|
34660
|
+
const args = ["calendar", "update", pos(calendarId), pos(eventId)];
|
|
34209
34661
|
if (summary !== void 0) args.push(`--summary=${summary}`);
|
|
34210
34662
|
if (from !== void 0) args.push(`--from=${from}`);
|
|
34211
34663
|
if (to !== void 0) args.push(`--to=${to}`);
|
|
@@ -34230,7 +34682,7 @@ function registerCalendarTools(server) {
|
|
|
34230
34682
|
account: accountParam
|
|
34231
34683
|
})
|
|
34232
34684
|
}, async ({ calendarId, eventId, account }) => {
|
|
34233
|
-
return runOrDiagnose(["calendar", "delete", calendarId, eventId, "--force"], { account });
|
|
34685
|
+
return runOrDiagnose(["calendar", "delete", pos(calendarId), pos(eventId), "--force"], { account });
|
|
34234
34686
|
});
|
|
34235
34687
|
server.registerTool("gog_calendar_respond", {
|
|
34236
34688
|
description: "Respond to a calendar event invitation.",
|
|
@@ -34243,7 +34695,7 @@ function registerCalendarTools(server) {
|
|
|
34243
34695
|
account: accountParam
|
|
34244
34696
|
})
|
|
34245
34697
|
}, async ({ calendarId, eventId, status, comment, account }) => {
|
|
34246
|
-
const args = ["calendar", "respond", calendarId, eventId, `--status=${status}`];
|
|
34698
|
+
const args = ["calendar", "respond", pos(calendarId), pos(eventId), `--status=${status}`];
|
|
34247
34699
|
if (comment) args.push(`--comment=${comment}`);
|
|
34248
34700
|
return runOrDiagnose(args, { account });
|
|
34249
34701
|
});
|
|
@@ -34258,6 +34710,7 @@ var MAX_REQUEST_PAYLOAD_WIRE_BYTES = REQUEST_MAX_BODY_BYTES - REQUEST_BODY_JSON_
|
|
|
34258
34710
|
var MAX_INLINE_ATTACHMENT_TOTAL_BYTES = Math.floor(MAX_REQUEST_PAYLOAD_WIRE_BYTES * 3 / 4);
|
|
34259
34711
|
function wireBytesOf(arg) {
|
|
34260
34712
|
if (typeof arg === "string") return Buffer.byteLength(arg, "utf8");
|
|
34713
|
+
if (arg.kind === "positional") return Buffer.byteLength(arg.value, "utf8");
|
|
34261
34714
|
return arg.encoding === "base64" ? arg.contents.length : Buffer.byteLength(arg.contents, "utf8");
|
|
34262
34715
|
}
|
|
34263
34716
|
var formatMiB = (bytes) => `${Math.floor(bytes / (1024 * 1024))} MiB`;
|
|
@@ -34360,7 +34813,7 @@ function registerChatTools(server) {
|
|
|
34360
34813
|
account: accountParam
|
|
34361
34814
|
})
|
|
34362
34815
|
}, async ({ displayName, exact, max, account }) => {
|
|
34363
|
-
const args = ["chat", "spaces", "find", displayName];
|
|
34816
|
+
const args = ["chat", "spaces", "find", pos(displayName)];
|
|
34364
34817
|
if (exact) args.push("--exact");
|
|
34365
34818
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
34366
34819
|
return runOrDiagnose(args, { account });
|
|
@@ -34374,7 +34827,7 @@ function registerChatTools(server) {
|
|
|
34374
34827
|
account: accountParam
|
|
34375
34828
|
})
|
|
34376
34829
|
}, async ({ displayName, members: members2, account }) => {
|
|
34377
|
-
const args = ["chat", "spaces", "create", displayName];
|
|
34830
|
+
const args = ["chat", "spaces", "create", pos(displayName)];
|
|
34378
34831
|
if (members2) for (const member of members2) args.push(`--member=${member}`);
|
|
34379
34832
|
return runOrDiagnose(args, { account });
|
|
34380
34833
|
});
|
|
@@ -34387,7 +34840,7 @@ function registerChatTools(server) {
|
|
|
34387
34840
|
account: accountParam
|
|
34388
34841
|
})
|
|
34389
34842
|
}, async ({ space, max, pageToken, page, all, account }) => {
|
|
34390
|
-
const args = ["chat", "threads", "list", space];
|
|
34843
|
+
const args = ["chat", "threads", "list", pos(space)];
|
|
34391
34844
|
pushPaginationFlags(args, { max, pageToken, page, all });
|
|
34392
34845
|
return runOrDiagnose(args, { account });
|
|
34393
34846
|
});
|
|
@@ -34403,7 +34856,7 @@ function registerChatTools(server) {
|
|
|
34403
34856
|
account: accountParam
|
|
34404
34857
|
})
|
|
34405
34858
|
}, async ({ space, thread, unread, order, max, pageToken, page, all, account }) => {
|
|
34406
|
-
const args = ["chat", "messages", "list", space];
|
|
34859
|
+
const args = ["chat", "messages", "list", pos(space)];
|
|
34407
34860
|
if (thread) args.push(`--thread=${thread}`);
|
|
34408
34861
|
if (unread) args.push("--unread");
|
|
34409
34862
|
if (order) args.push(`--order=${order}`);
|
|
@@ -34427,7 +34880,7 @@ function registerChatTools(server) {
|
|
|
34427
34880
|
account: accountParam
|
|
34428
34881
|
})
|
|
34429
34882
|
}, async ({ query, order, view, markup, max, pageToken, page, all, account }) => {
|
|
34430
|
-
const args = ["chat", "messages", "search", query];
|
|
34883
|
+
const args = ["chat", "messages", "search", pos(query)];
|
|
34431
34884
|
if (order) args.push(`--order=${order}`);
|
|
34432
34885
|
if (view) args.push(`--view=${view}`);
|
|
34433
34886
|
if (markup) args.push(`--markup=${markup}`);
|
|
@@ -34442,16 +34895,17 @@ function registerChatTools(server) {
|
|
|
34442
34895
|
text: external_exports.string().optional().describe("Message text. Optional only when an attachment is supplied."),
|
|
34443
34896
|
thread: threadParam,
|
|
34444
34897
|
attach: external_exports.array(external_exports.string()).optional().describe(
|
|
34445
|
-
"Attachment file paths, read WHERE GOG RUNS. On a hosted or remote deployment that is not your machine \u2014 use attachInline there instead."
|
|
34898
|
+
"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)."
|
|
34446
34899
|
),
|
|
34447
34900
|
attachInline: attachInlineParam,
|
|
34448
34901
|
account: accountParam
|
|
34449
34902
|
})
|
|
34450
34903
|
}, async ({ space, text, thread, attach, attachInline, account }) => {
|
|
34904
|
+
confinePaths(attach, "attach");
|
|
34451
34905
|
if (text === void 0 && !attach?.length && !attachInline?.length) {
|
|
34452
34906
|
throw new Error("A Chat message needs text, an attachment, or both.");
|
|
34453
34907
|
}
|
|
34454
|
-
const args = ["chat", "messages", "send", space];
|
|
34908
|
+
const args = ["chat", "messages", "send", pos(space)];
|
|
34455
34909
|
if (text !== void 0) args.push(`--text=${text}`);
|
|
34456
34910
|
if (thread) args.push(`--thread=${thread}`);
|
|
34457
34911
|
if (attach) for (const path of attach) args.push(`--attach=${path}`);
|
|
@@ -34468,7 +34922,7 @@ function registerChatTools(server) {
|
|
|
34468
34922
|
account: accountParam
|
|
34469
34923
|
})
|
|
34470
34924
|
}, async ({ email: email3, text, thread, account }) => {
|
|
34471
|
-
const args = ["chat", "dm", "send", email3, `--text=${text}`];
|
|
34925
|
+
const args = ["chat", "dm", "send", pos(email3), `--text=${text}`];
|
|
34472
34926
|
if (thread) args.push(`--thread=${thread}`);
|
|
34473
34927
|
return runOrDiagnose(args, { account });
|
|
34474
34928
|
});
|
|
@@ -34480,7 +34934,7 @@ function registerChatTools(server) {
|
|
|
34480
34934
|
account: accountParam
|
|
34481
34935
|
})
|
|
34482
34936
|
}, async ({ email: email3, account }) => {
|
|
34483
|
-
return runOrDiagnose(["chat", "dm", "space", email3], { account });
|
|
34937
|
+
return runOrDiagnose(["chat", "dm", "space", pos(email3)], { account });
|
|
34484
34938
|
});
|
|
34485
34939
|
server.registerTool("gog_chat_reactions_list", {
|
|
34486
34940
|
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,
|
|
@@ -34492,7 +34946,7 @@ function registerChatTools(server) {
|
|
|
34492
34946
|
account: accountParam
|
|
34493
34947
|
})
|
|
34494
34948
|
}, async ({ message, space, max, pageToken, page, all, account }) => {
|
|
34495
|
-
const args = ["chat", "messages", "reactions", "list", message];
|
|
34949
|
+
const args = ["chat", "messages", "reactions", "list", pos(message)];
|
|
34496
34950
|
if (space) args.push(`--space=${space}`);
|
|
34497
34951
|
pushPaginationFlags(args, { max, pageToken, page, all });
|
|
34498
34952
|
return runOrDiagnose(args, { account });
|
|
@@ -34507,7 +34961,7 @@ function registerChatTools(server) {
|
|
|
34507
34961
|
account: accountParam
|
|
34508
34962
|
})
|
|
34509
34963
|
}, async ({ message, emoji: emoji3, space, account }) => {
|
|
34510
|
-
const args = ["chat", "messages", "reactions", "create", message, emoji3];
|
|
34964
|
+
const args = ["chat", "messages", "reactions", "create", pos(message), pos(emoji3)];
|
|
34511
34965
|
if (space) args.push(`--space=${space}`);
|
|
34512
34966
|
return runOrDiagnose(args, { account });
|
|
34513
34967
|
});
|
|
@@ -34519,7 +34973,7 @@ function registerChatTools(server) {
|
|
|
34519
34973
|
account: accountParam
|
|
34520
34974
|
})
|
|
34521
34975
|
}, async ({ reaction, account }) => {
|
|
34522
|
-
return runOrDiagnose(["chat", "messages", "reactions", "delete", reaction], { account });
|
|
34976
|
+
return runOrDiagnose(["chat", "messages", "reactions", "delete", pos(reaction)], { account });
|
|
34523
34977
|
});
|
|
34524
34978
|
registerRunTool(server, {
|
|
34525
34979
|
service: "chat",
|
|
@@ -34562,7 +35016,7 @@ function registerClassroomTools(server) {
|
|
|
34562
35016
|
account: accountParam
|
|
34563
35017
|
})
|
|
34564
35018
|
}, async ({ courseId, account }) => {
|
|
34565
|
-
return runOrDiagnose(["classroom", "courses", "get", courseId], { account });
|
|
35019
|
+
return runOrDiagnose(["classroom", "courses", "get", pos(courseId)], { account });
|
|
34566
35020
|
});
|
|
34567
35021
|
server.registerTool("gog_classroom_students_list", {
|
|
34568
35022
|
description: "List students enrolled in a Google Classroom course.",
|
|
@@ -34576,7 +35030,7 @@ function registerClassroomTools(server) {
|
|
|
34576
35030
|
account: accountParam
|
|
34577
35031
|
})
|
|
34578
35032
|
}, async ({ courseId, max, pageToken, page, all, account }) => {
|
|
34579
|
-
const args = ["classroom", "students", "list", courseId];
|
|
35033
|
+
const args = ["classroom", "students", "list", pos(courseId)];
|
|
34580
35034
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
34581
35035
|
const token = resolvePageToken({ pageToken, page });
|
|
34582
35036
|
if (token) args.push(`--page=${token}`);
|
|
@@ -34592,7 +35046,7 @@ function registerClassroomTools(server) {
|
|
|
34592
35046
|
account: accountParam
|
|
34593
35047
|
})
|
|
34594
35048
|
}, async ({ courseId, userId, account }) => {
|
|
34595
|
-
return runOrDiagnose(["classroom", "students", "get", courseId, userId], { account });
|
|
35049
|
+
return runOrDiagnose(["classroom", "students", "get", pos(courseId), pos(userId)], { account });
|
|
34596
35050
|
});
|
|
34597
35051
|
server.registerTool("gog_classroom_teachers_list", {
|
|
34598
35052
|
description: "List teachers in a Google Classroom course.",
|
|
@@ -34606,7 +35060,7 @@ function registerClassroomTools(server) {
|
|
|
34606
35060
|
account: accountParam
|
|
34607
35061
|
})
|
|
34608
35062
|
}, async ({ courseId, max, pageToken, page, all, account }) => {
|
|
34609
|
-
const args = ["classroom", "teachers", "list", courseId];
|
|
35063
|
+
const args = ["classroom", "teachers", "list", pos(courseId)];
|
|
34610
35064
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
34611
35065
|
const token = resolvePageToken({ pageToken, page });
|
|
34612
35066
|
if (token) args.push(`--page=${token}`);
|
|
@@ -34622,7 +35076,7 @@ function registerClassroomTools(server) {
|
|
|
34622
35076
|
account: accountParam
|
|
34623
35077
|
})
|
|
34624
35078
|
}, async ({ courseId, userId, account }) => {
|
|
34625
|
-
return runOrDiagnose(["classroom", "teachers", "get", courseId, userId], { account });
|
|
35079
|
+
return runOrDiagnose(["classroom", "teachers", "get", pos(courseId), pos(userId)], { account });
|
|
34626
35080
|
});
|
|
34627
35081
|
server.registerTool("gog_classroom_roster", {
|
|
34628
35082
|
description: "List the full roster (students and/or teachers) of a Google Classroom course. Omit both flags to return both groups.",
|
|
@@ -34638,7 +35092,7 @@ function registerClassroomTools(server) {
|
|
|
34638
35092
|
account: accountParam
|
|
34639
35093
|
})
|
|
34640
35094
|
}, async ({ courseId, students, teachers, max, pageToken, page, all, account }) => {
|
|
34641
|
-
const args = ["classroom", "roster", courseId];
|
|
35095
|
+
const args = ["classroom", "roster", pos(courseId)];
|
|
34642
35096
|
if (students) args.push("--students");
|
|
34643
35097
|
if (teachers) args.push("--teachers");
|
|
34644
35098
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
@@ -34663,7 +35117,7 @@ function registerClassroomTools(server) {
|
|
|
34663
35117
|
account: accountParam
|
|
34664
35118
|
})
|
|
34665
35119
|
}, async ({ courseId, state, topic, orderBy, max, pageToken, page, all, scanPages, account }) => {
|
|
34666
|
-
const args = ["classroom", "coursework", "list", courseId];
|
|
35120
|
+
const args = ["classroom", "coursework", "list", pos(courseId)];
|
|
34667
35121
|
if (state) args.push(`--state=${state}`);
|
|
34668
35122
|
if (topic) args.push(`--topic=${topic}`);
|
|
34669
35123
|
if (orderBy) args.push(`--order-by=${orderBy}`);
|
|
@@ -34683,7 +35137,7 @@ function registerClassroomTools(server) {
|
|
|
34683
35137
|
account: accountParam
|
|
34684
35138
|
})
|
|
34685
35139
|
}, async ({ courseId, courseworkId, account }) => {
|
|
34686
|
-
return runOrDiagnose(["classroom", "coursework", "get", courseId, courseworkId], { account });
|
|
35140
|
+
return runOrDiagnose(["classroom", "coursework", "get", pos(courseId), pos(courseworkId)], { account });
|
|
34687
35141
|
});
|
|
34688
35142
|
server.registerTool("gog_classroom_submissions_list", {
|
|
34689
35143
|
description: "List student submissions for a coursework item.",
|
|
@@ -34701,7 +35155,7 @@ function registerClassroomTools(server) {
|
|
|
34701
35155
|
account: accountParam
|
|
34702
35156
|
})
|
|
34703
35157
|
}, async ({ courseId, courseworkId, state, late, user, max, pageToken, page, all, account }) => {
|
|
34704
|
-
const args = ["classroom", "submissions", "list", courseId, courseworkId];
|
|
35158
|
+
const args = ["classroom", "submissions", "list", pos(courseId), pos(courseworkId)];
|
|
34705
35159
|
if (state) args.push(`--state=${state}`);
|
|
34706
35160
|
if (late) args.push(`--late=${late}`);
|
|
34707
35161
|
if (user) args.push(`--user=${user}`);
|
|
@@ -34721,7 +35175,7 @@ function registerClassroomTools(server) {
|
|
|
34721
35175
|
account: accountParam
|
|
34722
35176
|
})
|
|
34723
35177
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
34724
|
-
return runOrDiagnose(["classroom", "submissions", "get", courseId, courseworkId, submissionId], { account });
|
|
35178
|
+
return runOrDiagnose(["classroom", "submissions", "get", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
34725
35179
|
});
|
|
34726
35180
|
server.registerTool("gog_classroom_submissions_grade", {
|
|
34727
35181
|
description: "Grade a student submission. Set draft and/or assigned grade values.",
|
|
@@ -34735,7 +35189,7 @@ function registerClassroomTools(server) {
|
|
|
34735
35189
|
account: accountParam
|
|
34736
35190
|
})
|
|
34737
35191
|
}, async ({ courseId, courseworkId, submissionId, draft, assigned, account }) => {
|
|
34738
|
-
const args = ["classroom", "submissions", "grade", courseId, courseworkId, submissionId];
|
|
35192
|
+
const args = ["classroom", "submissions", "grade", pos(courseId), pos(courseworkId), pos(submissionId)];
|
|
34739
35193
|
if (draft) args.push(`--draft=${draft}`);
|
|
34740
35194
|
if (assigned) args.push(`--assigned=${assigned}`);
|
|
34741
35195
|
return runOrDiagnose(args, { account });
|
|
@@ -34750,7 +35204,7 @@ function registerClassroomTools(server) {
|
|
|
34750
35204
|
account: accountParam
|
|
34751
35205
|
})
|
|
34752
35206
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
34753
|
-
return runOrDiagnose(["classroom", "submissions", "return", courseId, courseworkId, submissionId], { account });
|
|
35207
|
+
return runOrDiagnose(["classroom", "submissions", "return", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
34754
35208
|
});
|
|
34755
35209
|
server.registerTool("gog_classroom_submissions_turn_in", {
|
|
34756
35210
|
description: "Turn in a student submission (student action).",
|
|
@@ -34762,7 +35216,7 @@ function registerClassroomTools(server) {
|
|
|
34762
35216
|
account: accountParam
|
|
34763
35217
|
})
|
|
34764
35218
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
34765
|
-
return runOrDiagnose(["classroom", "submissions", "turn-in", courseId, courseworkId, submissionId], { account });
|
|
35219
|
+
return runOrDiagnose(["classroom", "submissions", "turn-in", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
34766
35220
|
});
|
|
34767
35221
|
server.registerTool("gog_classroom_submissions_reclaim", {
|
|
34768
35222
|
description: "Reclaim a turned-in submission (student action to edit a submission).",
|
|
@@ -34774,7 +35228,7 @@ function registerClassroomTools(server) {
|
|
|
34774
35228
|
account: accountParam
|
|
34775
35229
|
})
|
|
34776
35230
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
34777
|
-
return runOrDiagnose(["classroom", "submissions", "reclaim", courseId, courseworkId, submissionId], { account });
|
|
35231
|
+
return runOrDiagnose(["classroom", "submissions", "reclaim", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
34778
35232
|
});
|
|
34779
35233
|
server.registerTool("gog_classroom_announcements_list", {
|
|
34780
35234
|
description: "List announcements in a Google Classroom course.",
|
|
@@ -34790,7 +35244,7 @@ function registerClassroomTools(server) {
|
|
|
34790
35244
|
account: accountParam
|
|
34791
35245
|
})
|
|
34792
35246
|
}, async ({ courseId, state, orderBy, max, pageToken, page, all, account }) => {
|
|
34793
|
-
const args = ["classroom", "announcements", "list", courseId];
|
|
35247
|
+
const args = ["classroom", "announcements", "list", pos(courseId)];
|
|
34794
35248
|
if (state) args.push(`--state=${state}`);
|
|
34795
35249
|
if (orderBy) args.push(`--order-by=${orderBy}`);
|
|
34796
35250
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
@@ -34808,7 +35262,7 @@ function registerClassroomTools(server) {
|
|
|
34808
35262
|
account: accountParam
|
|
34809
35263
|
})
|
|
34810
35264
|
}, async ({ courseId, announcementId, account }) => {
|
|
34811
|
-
return runOrDiagnose(["classroom", "announcements", "get", courseId, announcementId], { account });
|
|
35265
|
+
return runOrDiagnose(["classroom", "announcements", "get", pos(courseId), pos(announcementId)], { account });
|
|
34812
35266
|
});
|
|
34813
35267
|
server.registerTool("gog_classroom_announcements_create", {
|
|
34814
35268
|
description: "Create an announcement in a Google Classroom course.",
|
|
@@ -34821,7 +35275,7 @@ function registerClassroomTools(server) {
|
|
|
34821
35275
|
account: accountParam
|
|
34822
35276
|
})
|
|
34823
35277
|
}, async ({ courseId, text, state, scheduled, account }) => {
|
|
34824
|
-
const args = ["classroom", "announcements", "create", courseId, `--text=${text}`];
|
|
35278
|
+
const args = ["classroom", "announcements", "create", pos(courseId), `--text=${text}`];
|
|
34825
35279
|
if (state) args.push(`--state=${state}`);
|
|
34826
35280
|
if (scheduled) args.push(`--scheduled=${scheduled}`);
|
|
34827
35281
|
return runOrDiagnose(args, { account });
|
|
@@ -34838,7 +35292,7 @@ function registerClassroomTools(server) {
|
|
|
34838
35292
|
account: accountParam
|
|
34839
35293
|
})
|
|
34840
35294
|
}, async ({ courseId, max, pageToken, page, all, account }) => {
|
|
34841
|
-
const args = ["classroom", "topics", "list", courseId];
|
|
35295
|
+
const args = ["classroom", "topics", "list", pos(courseId)];
|
|
34842
35296
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
34843
35297
|
const token = resolvePageToken({ pageToken, page });
|
|
34844
35298
|
if (token) args.push(`--page=${token}`);
|
|
@@ -34854,7 +35308,7 @@ function registerClassroomTools(server) {
|
|
|
34854
35308
|
account: accountParam
|
|
34855
35309
|
})
|
|
34856
35310
|
}, async ({ courseId, topicId, account }) => {
|
|
34857
|
-
return runOrDiagnose(["classroom", "topics", "get", courseId, topicId], { account });
|
|
35311
|
+
return runOrDiagnose(["classroom", "topics", "get", pos(courseId), pos(topicId)], { account });
|
|
34858
35312
|
});
|
|
34859
35313
|
server.registerTool("gog_classroom_invitations_list", {
|
|
34860
35314
|
description: "List Google Classroom invitations.",
|
|
@@ -34886,7 +35340,7 @@ function registerClassroomTools(server) {
|
|
|
34886
35340
|
account: accountParam
|
|
34887
35341
|
})
|
|
34888
35342
|
}, async ({ invitationId, account }) => {
|
|
34889
|
-
return runOrDiagnose(["classroom", "invitations", "get", invitationId], { account });
|
|
35343
|
+
return runOrDiagnose(["classroom", "invitations", "get", pos(invitationId)], { account });
|
|
34890
35344
|
});
|
|
34891
35345
|
server.registerTool("gog_classroom_invitations_accept", {
|
|
34892
35346
|
description: "Accept a Google Classroom invitation.",
|
|
@@ -34896,7 +35350,7 @@ function registerClassroomTools(server) {
|
|
|
34896
35350
|
account: accountParam
|
|
34897
35351
|
})
|
|
34898
35352
|
}, async ({ invitationId, account }) => {
|
|
34899
|
-
return runOrDiagnose(["classroom", "invitations", "accept", invitationId], { account });
|
|
35353
|
+
return runOrDiagnose(["classroom", "invitations", "accept", pos(invitationId)], { account });
|
|
34900
35354
|
});
|
|
34901
35355
|
server.registerTool("gog_classroom_profile_get", {
|
|
34902
35356
|
description: "Get a Google Classroom user profile. Omit userId to fetch the authenticated user.",
|
|
@@ -34907,7 +35361,7 @@ function registerClassroomTools(server) {
|
|
|
34907
35361
|
})
|
|
34908
35362
|
}, async ({ userId, account }) => {
|
|
34909
35363
|
const args = ["classroom", "profile", "get"];
|
|
34910
|
-
if (userId) args.push(userId);
|
|
35364
|
+
if (userId) args.push(pos(userId));
|
|
34911
35365
|
return runOrDiagnose(args, { account });
|
|
34912
35366
|
});
|
|
34913
35367
|
registerRunTool(server, {
|
|
@@ -34927,7 +35381,7 @@ function registerContactsTools(server) {
|
|
|
34927
35381
|
account: accountParam
|
|
34928
35382
|
})
|
|
34929
35383
|
}, async ({ query, account }) => {
|
|
34930
|
-
return runOrDiagnose(["contacts", "search", query], { account });
|
|
35384
|
+
return runOrDiagnose(["contacts", "search", pos(query)], { account });
|
|
34931
35385
|
});
|
|
34932
35386
|
server.registerTool("gog_contacts_list", {
|
|
34933
35387
|
description: "List all Google Contacts.",
|
|
@@ -34946,7 +35400,7 @@ function registerContactsTools(server) {
|
|
|
34946
35400
|
account: accountParam
|
|
34947
35401
|
})
|
|
34948
35402
|
}, async ({ resourceName, account }) => {
|
|
34949
|
-
return runOrDiagnose(["contacts", "get", resourceName], { account });
|
|
35403
|
+
return runOrDiagnose(["contacts", "get", pos(resourceName)], { account });
|
|
34950
35404
|
});
|
|
34951
35405
|
server.registerTool("gog_contacts_create", {
|
|
34952
35406
|
description: "Create a new Google Contact.",
|
|
@@ -34982,7 +35436,7 @@ function registerDocsTools(server) {
|
|
|
34982
35436
|
account: accountParam
|
|
34983
35437
|
})
|
|
34984
35438
|
}, async ({ docId, account }) => {
|
|
34985
|
-
return runOrDiagnose(["docs", "info", docId], { account });
|
|
35439
|
+
return runOrDiagnose(["docs", "info", pos(docId)], { account });
|
|
34986
35440
|
});
|
|
34987
35441
|
server.registerTool("gog_docs_cat", {
|
|
34988
35442
|
description: "Read a Google Doc as plain text.",
|
|
@@ -34993,7 +35447,7 @@ function registerDocsTools(server) {
|
|
|
34993
35447
|
account: accountParam
|
|
34994
35448
|
})
|
|
34995
35449
|
}, async ({ docId, chips, account }) => {
|
|
34996
|
-
const args = ["docs", "cat", docId];
|
|
35450
|
+
const args = ["docs", "cat", pos(docId)];
|
|
34997
35451
|
if (chips) args.push("--chips");
|
|
34998
35452
|
return runOrDiagnose(args, { account });
|
|
34999
35453
|
});
|
|
@@ -35005,7 +35459,7 @@ function registerDocsTools(server) {
|
|
|
35005
35459
|
account: accountParam
|
|
35006
35460
|
})
|
|
35007
35461
|
}, async ({ title, account }) => {
|
|
35008
|
-
return runOrDiagnose(["docs", "create", title], { account });
|
|
35462
|
+
return runOrDiagnose(["docs", "create", pos(title)], { account });
|
|
35009
35463
|
});
|
|
35010
35464
|
server.registerTool("gog_docs_write", {
|
|
35011
35465
|
description: "Write text content to a Google Doc, replacing existing body content by default. Set append=true to add after existing content.",
|
|
@@ -35030,7 +35484,7 @@ function registerDocsTools(server) {
|
|
|
35030
35484
|
account: accountParam
|
|
35031
35485
|
})
|
|
35032
35486
|
}, async ({ docId, text, append, checkOrphans, bullets, bulletPreset, ordered, noBullets, indentStart, indentEnd, indentFirstLine, spaceAbove, spaceBelow, keepLinesTogether, keepWithNext, batch, account }) => {
|
|
35033
|
-
const args = ["docs", "write", docId, `--text=${text}`];
|
|
35487
|
+
const args = ["docs", "write", pos(docId), `--text=${text}`];
|
|
35034
35488
|
if (append) args.push("--append");
|
|
35035
35489
|
if (checkOrphans) args.push("--check-orphans");
|
|
35036
35490
|
if (bullets) args.push("--bullets");
|
|
@@ -35057,7 +35511,7 @@ function registerDocsTools(server) {
|
|
|
35057
35511
|
account: accountParam
|
|
35058
35512
|
})
|
|
35059
35513
|
}, async ({ docId, find, replace, account }) => {
|
|
35060
|
-
return runOrDiagnose(["docs", "find-replace", docId, find, replace], { account });
|
|
35514
|
+
return runOrDiagnose(["docs", "find-replace", pos(docId), pos(find), pos(replace)], { account });
|
|
35061
35515
|
});
|
|
35062
35516
|
server.registerTool("gog_docs_structure", {
|
|
35063
35517
|
description: "Show a Google Doc's structure with numbered paragraphs. Useful for understanding the document layout before making index-based edits.",
|
|
@@ -35067,7 +35521,7 @@ function registerDocsTools(server) {
|
|
|
35067
35521
|
account: accountParam
|
|
35068
35522
|
})
|
|
35069
35523
|
}, async ({ docId, account }) => {
|
|
35070
|
-
return runOrDiagnose(["docs", "structure", docId], { account });
|
|
35524
|
+
return runOrDiagnose(["docs", "structure", pos(docId)], { account });
|
|
35071
35525
|
});
|
|
35072
35526
|
registerRunTool(server, { service: "docs", examples: '"copy", "clear", "insert", "sed", "export"' });
|
|
35073
35527
|
}
|
|
@@ -35077,8 +35531,10 @@ var GOOGLE_DOC_MIME = "application/vnd.google-apps.document";
|
|
|
35077
35531
|
function fileMeta(raw) {
|
|
35078
35532
|
const parsed = JSON.parse(raw);
|
|
35079
35533
|
const f = parsed.file ?? parsed;
|
|
35080
|
-
|
|
35534
|
+
const size = f.size === void 0 ? void 0 : Number(f.size);
|
|
35535
|
+
return { name: f.name, mimeType: f.mimeType, size: Number.isFinite(size) ? size : void 0 };
|
|
35081
35536
|
}
|
|
35537
|
+
var MAX_DRIVE_READ_BYTES = MAX_INLINE_ATTACHMENT_BYTES;
|
|
35082
35538
|
var DRIVE_LS_COMPACT_FIELDS = "nextPageToken,files(id,name,mimeType,modifiedTime,size,webViewLink)";
|
|
35083
35539
|
function registerDriveTools(server) {
|
|
35084
35540
|
server.registerTool("gog_drive_ls", {
|
|
@@ -35122,7 +35578,7 @@ function registerDriveTools(server) {
|
|
|
35122
35578
|
account: accountParam
|
|
35123
35579
|
})
|
|
35124
35580
|
}, async ({ query, view, account }) => {
|
|
35125
|
-
return runOrDiagnose(["drive", "search", query], {
|
|
35581
|
+
return runOrDiagnose(["drive", "search", pos(query)], {
|
|
35126
35582
|
account,
|
|
35127
35583
|
stripMedia: resolveView(view, ["compact", "full"]) === "compact"
|
|
35128
35584
|
});
|
|
@@ -35142,7 +35598,7 @@ function registerDriveTools(server) {
|
|
|
35142
35598
|
account: accountParam
|
|
35143
35599
|
})
|
|
35144
35600
|
}, async ({ fileId, view, account }) => {
|
|
35145
|
-
return runOrDiagnose(["drive", "get", fileId], {
|
|
35601
|
+
return runOrDiagnose(["drive", "get", pos(fileId)], {
|
|
35146
35602
|
account,
|
|
35147
35603
|
stripMedia: resolveView(view, ["compact", "full"]) === "compact"
|
|
35148
35604
|
});
|
|
@@ -35155,7 +35611,7 @@ function registerDriveTools(server) {
|
|
|
35155
35611
|
account: accountParam
|
|
35156
35612
|
})
|
|
35157
35613
|
}, async ({ name, account }) => {
|
|
35158
|
-
return runOrDiagnose(["drive", "mkdir", name], { account });
|
|
35614
|
+
return runOrDiagnose(["drive", "mkdir", pos(name)], { account });
|
|
35159
35615
|
});
|
|
35160
35616
|
server.registerTool("gog_drive_rename", {
|
|
35161
35617
|
description: "Rename a file or folder in Google Drive.",
|
|
@@ -35166,7 +35622,7 @@ function registerDriveTools(server) {
|
|
|
35166
35622
|
account: accountParam
|
|
35167
35623
|
})
|
|
35168
35624
|
}, async ({ fileId, newName, account }) => {
|
|
35169
|
-
return runOrDiagnose(["drive", "rename", fileId, newName], { account });
|
|
35625
|
+
return runOrDiagnose(["drive", "rename", pos(fileId), pos(newName)], { account });
|
|
35170
35626
|
});
|
|
35171
35627
|
server.registerTool("gog_drive_move", {
|
|
35172
35628
|
description: "Move a file to a different folder in Google Drive.",
|
|
@@ -35177,7 +35633,7 @@ function registerDriveTools(server) {
|
|
|
35177
35633
|
account: accountParam
|
|
35178
35634
|
})
|
|
35179
35635
|
}, async ({ fileId, parentId, account }) => {
|
|
35180
|
-
return runOrDiagnose(["drive", "move", fileId, `--parent=${parentId}`], { account });
|
|
35636
|
+
return runOrDiagnose(["drive", "move", pos(fileId), `--parent=${parentId}`], { account });
|
|
35181
35637
|
});
|
|
35182
35638
|
server.registerTool("gog_drive_delete", {
|
|
35183
35639
|
description: "Move a Google Drive file to trash, or permanently delete it with permanent=true (irreversible).",
|
|
@@ -35188,7 +35644,7 @@ function registerDriveTools(server) {
|
|
|
35188
35644
|
account: accountParam
|
|
35189
35645
|
})
|
|
35190
35646
|
}, async ({ fileId, permanent, account }) => {
|
|
35191
|
-
const args = ["drive", "delete", fileId];
|
|
35647
|
+
const args = ["drive", "delete", pos(fileId)];
|
|
35192
35648
|
if (permanent) args.push("--permanent");
|
|
35193
35649
|
args.push("--force");
|
|
35194
35650
|
return runOrDiagnose(args, { account });
|
|
@@ -35205,7 +35661,7 @@ function registerDriveTools(server) {
|
|
|
35205
35661
|
account: accountParam
|
|
35206
35662
|
})
|
|
35207
35663
|
}, async ({ fileId, to, email: email3, domain: domain2, role, account }) => {
|
|
35208
|
-
const args = ["drive", "share", fileId, `--to=${to}`];
|
|
35664
|
+
const args = ["drive", "share", pos(fileId), `--to=${to}`];
|
|
35209
35665
|
if (email3) args.push(`--email=${email3}`);
|
|
35210
35666
|
if (domain2) args.push(`--domain=${domain2}`);
|
|
35211
35667
|
if (role) args.push(`--role=${role}`);
|
|
@@ -35228,7 +35684,7 @@ function registerDriveTools(server) {
|
|
|
35228
35684
|
}, async ({ fileId, ocrLanguage, offset = 0, maxChars, account }) => {
|
|
35229
35685
|
let tempDocId;
|
|
35230
35686
|
try {
|
|
35231
|
-
const { name, mimeType } = fileMeta(await run(["drive", "get", fileId], { account }));
|
|
35687
|
+
const { name, mimeType } = fileMeta(await run(["drive", "get", pos(fileId)], { account }));
|
|
35232
35688
|
let sourceId = fileId;
|
|
35233
35689
|
if (mimeType !== GOOGLE_DOC_MIME) {
|
|
35234
35690
|
const params = JSON.stringify({ fileId, ...ocrLanguage ? { ocrLanguage } : {} });
|
|
@@ -35273,13 +35729,13 @@ function registerDriveTools(server) {
|
|
|
35273
35729
|
return diagnose(err);
|
|
35274
35730
|
} finally {
|
|
35275
35731
|
if (tempDocId) {
|
|
35276
|
-
await run(["drive", "delete", tempDocId, "--permanent", "--force"], { account }).catch(() => {
|
|
35732
|
+
await run(["drive", "delete", pos(tempDocId), "--permanent", "--force"], { account }).catch(() => {
|
|
35277
35733
|
});
|
|
35278
35734
|
}
|
|
35279
35735
|
}
|
|
35280
35736
|
});
|
|
35281
35737
|
server.registerTool("gog_drive_read_bytes", {
|
|
35282
|
-
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.",
|
|
35738
|
+
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.",
|
|
35283
35739
|
annotations: { readOnlyHint: true },
|
|
35284
35740
|
inputSchema: external_exports.object({
|
|
35285
35741
|
fileId: external_exports.string().describe("Drive file ID"),
|
|
@@ -35287,9 +35743,17 @@ function registerDriveTools(server) {
|
|
|
35287
35743
|
})
|
|
35288
35744
|
}, async ({ fileId, account }) => {
|
|
35289
35745
|
try {
|
|
35290
|
-
const { name, mimeType } = fileMeta(await run(["drive", "get", fileId], { account }));
|
|
35746
|
+
const { name, mimeType, size } = fileMeta(await run(["drive", "get", pos(fileId)], { account }));
|
|
35747
|
+
if (size !== void 0 && size > MAX_DRIVE_READ_BYTES) {
|
|
35748
|
+
return errorResult(
|
|
35749
|
+
`${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.`
|
|
35750
|
+
);
|
|
35751
|
+
}
|
|
35291
35752
|
const params = JSON.stringify({ fileId, alt: "media" });
|
|
35292
|
-
const blob = await runBinary(
|
|
35753
|
+
const blob = await runBinary(
|
|
35754
|
+
["api", "call", "drive", "v3", "files.get", `--params=${params}`],
|
|
35755
|
+
{ account, maxOutputBytes: MAX_DRIVE_READ_BYTES }
|
|
35756
|
+
);
|
|
35293
35757
|
const type = mimeType ?? "application/octet-stream";
|
|
35294
35758
|
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).`;
|
|
35295
35759
|
return {
|
|
@@ -35309,7 +35773,7 @@ function registerDriveTools(server) {
|
|
|
35309
35773
|
return diagnose(err);
|
|
35310
35774
|
}
|
|
35311
35775
|
});
|
|
35312
|
-
registerRunTool(server, { service: "drive", examples: '"copy", "
|
|
35776
|
+
registerRunTool(server, { service: "drive", examples: '"copy", "download", "permissions"' });
|
|
35313
35777
|
}
|
|
35314
35778
|
|
|
35315
35779
|
// src/gmail-results.ts
|
|
@@ -35337,7 +35801,7 @@ async function countMatches(method, itemsKey, query, account) {
|
|
|
35337
35801
|
maxResults: COUNT_PROBE_PAGE_SIZE,
|
|
35338
35802
|
fields: `${itemsKey}/id,nextPageToken`
|
|
35339
35803
|
});
|
|
35340
|
-
const raw = await run(["api", "call", "gmail", "v1", method, `--params=${params}`], { account });
|
|
35804
|
+
const raw = await run(["api", "call", "gmail", "v1", pos(method), `--params=${params}`], { account });
|
|
35341
35805
|
const parsed = JSON.parse(raw);
|
|
35342
35806
|
const items = parsed[itemsKey];
|
|
35343
35807
|
if (!Array.isArray(items)) return {};
|
|
@@ -35376,7 +35840,7 @@ async function fetchGmailPages(runPage, itemsKey, maxPages, startToken) {
|
|
|
35376
35840
|
const result = await runPage(token);
|
|
35377
35841
|
const parsed = parsePage(result, itemsKey);
|
|
35378
35842
|
if (parsed === void 0) {
|
|
35379
|
-
return base === void 0 ? result : finish(base, itemsKey, merged, token);
|
|
35843
|
+
return base === void 0 ? result : finish(base, itemsKey, merged, token, pageErrorText(result, itemsKey, pages + 1));
|
|
35380
35844
|
}
|
|
35381
35845
|
base = parsed;
|
|
35382
35846
|
merged.push(...parsed[itemsKey]);
|
|
@@ -35407,10 +35871,18 @@ function parsePage(result, itemsKey) {
|
|
|
35407
35871
|
const obj = parsed;
|
|
35408
35872
|
return Array.isArray(obj[itemsKey]) ? obj : void 0;
|
|
35409
35873
|
}
|
|
35410
|
-
|
|
35874
|
+
var PAGE_ERROR_MAX = 2e3;
|
|
35875
|
+
function pageErrorText(result, itemsKey, n) {
|
|
35876
|
+
const first = result.content[0];
|
|
35877
|
+
const text = first?.type === "text" ? first.text.slice(0, PAGE_ERROR_MAX) : "";
|
|
35878
|
+
if (result.isError) return `page ${n} failed${text ? `: ${text}` : ""}`;
|
|
35879
|
+
return `page ${n} returned output that is not a ${itemsKey} list`;
|
|
35880
|
+
}
|
|
35881
|
+
function finish(base, itemsKey, merged, token, pageError) {
|
|
35411
35882
|
const out = { ...base, [itemsKey]: merged };
|
|
35412
35883
|
if (token === void 0) delete out.nextPageToken;
|
|
35413
35884
|
else out.nextPageToken = token;
|
|
35885
|
+
if (pageError !== void 0) out.pageError = pageError;
|
|
35414
35886
|
return rawTextResult(JSON.stringify(out));
|
|
35415
35887
|
}
|
|
35416
35888
|
|
|
@@ -35424,16 +35896,27 @@ var STAGING_TWIN = {
|
|
|
35424
35896
|
"gmail.reply-all": "gog_gmail_drafts_reply_all",
|
|
35425
35897
|
"gmail.send": "gog_gmail_drafts_create"
|
|
35426
35898
|
};
|
|
35899
|
+
var UNSUPPORTED_NOTE2 = {
|
|
35900
|
+
"gmail.drafts-send": "The draft is still saved: ask the user to review it and send it from Gmail."
|
|
35901
|
+
};
|
|
35902
|
+
var BODY_PREVIEW_MAX = 2048;
|
|
35903
|
+
function bodyPreview(text) {
|
|
35904
|
+
if (!text) return void 0;
|
|
35905
|
+
if (text.length <= BODY_PREVIEW_MAX) return text;
|
|
35906
|
+
return `${text.slice(0, BODY_PREVIEW_MAX)}\u2026 [${text.length - BODY_PREVIEW_MAX} more characters not shown]`;
|
|
35907
|
+
}
|
|
35908
|
+
function attachmentNames(paths, inline) {
|
|
35909
|
+
return [...paths ?? [], ...(inline ?? []).map((a) => a.filename)];
|
|
35910
|
+
}
|
|
35427
35911
|
function requireGmailDispatchConfirmation(ctx, op, details) {
|
|
35428
35912
|
const twin = STAGING_TWIN[op];
|
|
35913
|
+
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];
|
|
35429
35914
|
return requireConfirmation(ctx, {
|
|
35430
35915
|
action: op,
|
|
35431
|
-
message: "Review and confirm this email dispatch:",
|
|
35916
|
+
message: op === "gmail.filter-forward" ? "Review and confirm this mail-forwarding filter:" : "Review and confirm this email dispatch:",
|
|
35432
35917
|
details,
|
|
35433
|
-
confirmationLabel: "Confirm that this email should be sent now.",
|
|
35434
|
-
...
|
|
35435
|
-
unsupportedNote: `Stage it with ${twin} instead, review the draft, and send it with gog_gmail_drafts_send.`
|
|
35436
|
-
} : {}
|
|
35918
|
+
confirmationLabel: op === "gmail.filter-forward" ? "Confirm that matching mail should be forwarded automatically from now on." : "Confirm that this email should be sent now.",
|
|
35919
|
+
...note ? { unsupportedNote: note } : {}
|
|
35437
35920
|
});
|
|
35438
35921
|
}
|
|
35439
35922
|
var EMAIL_PATTERN = /[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(?:\.[a-z0-9-]+)+/gi;
|
|
@@ -35504,7 +35987,7 @@ var replySchema = {
|
|
|
35504
35987
|
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."),
|
|
35505
35988
|
subject: external_exports.string().optional().describe('Override reply subject (default: "Re: <original>"). A changed subject starts a NEW Gmail thread.'),
|
|
35506
35989
|
noQuote: external_exports.boolean().optional().describe("Do not include the original message quoted below the reply (default: the original is quoted)"),
|
|
35507
|
-
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.`),
|
|
35990
|
+
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).`),
|
|
35508
35991
|
attachInline: attachInlineParam,
|
|
35509
35992
|
from: external_exports.string().optional().describe("Send from this email address (must be a verified send-as alias)"),
|
|
35510
35993
|
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."),
|
|
@@ -35513,8 +35996,14 @@ var replySchema = {
|
|
|
35513
35996
|
signatureFile: external_exports.string().optional().describe("Append a local signature file (plain text or HTML), read on the gog server"),
|
|
35514
35997
|
account: accountParam
|
|
35515
35998
|
};
|
|
35999
|
+
function confineReplyPaths(f) {
|
|
36000
|
+
confinePaths(f.attach, "attach");
|
|
36001
|
+
if (f.bodyHtmlFile) confinePath(f.bodyHtmlFile, "bodyHtmlFile");
|
|
36002
|
+
if (f.signatureFile) confinePath(f.signatureFile, "signatureFile");
|
|
36003
|
+
}
|
|
35516
36004
|
function appendReplyFlags(args, f) {
|
|
35517
36005
|
assertNotBoth("bodyHtml", "bodyHtmlFile", f.bodyHtml, f.bodyHtmlFile);
|
|
36006
|
+
confineReplyPaths(f);
|
|
35518
36007
|
if (f.body) args.push(payloadArg("body", "body-file", f.body));
|
|
35519
36008
|
if (f.bodyHtml) args.push(payloadArg("body-html", "body-html-file", f.bodyHtml, "html"));
|
|
35520
36009
|
else if (f.bodyHtmlFile) args.push(`--body-html-file=${f.bodyHtmlFile}`);
|
|
@@ -35558,7 +36047,7 @@ function computeReplyRecipients(kind, headers, flags) {
|
|
|
35558
36047
|
return emails;
|
|
35559
36048
|
}
|
|
35560
36049
|
async function sendReply(kind, toolName, messageId, account, flags, ctx) {
|
|
35561
|
-
const metaResult = await runOrDiagnose(["gmail", "get", messageId, "--format=metadata"], { account });
|
|
36050
|
+
const metaResult = await runOrDiagnose(["gmail", "get", pos(messageId), "--format=metadata"], { account });
|
|
35562
36051
|
if (metaResult.isError) return metaResult;
|
|
35563
36052
|
const headers = parseMetadataHeaders(resultText(metaResult));
|
|
35564
36053
|
const recipients = computeReplyRecipients(kind, headers, flags);
|
|
@@ -35569,15 +36058,34 @@ async function sendReply(kind, toolName, messageId, account, flags, ctx) {
|
|
|
35569
36058
|
subject: flags.subject || (headers.subject ? `Re: ${headers.subject}` : void 0),
|
|
35570
36059
|
quoting: !flags.noQuote,
|
|
35571
36060
|
bodyLength: (flags.body ?? flags.bodyHtml ?? "").length,
|
|
35572
|
-
|
|
36061
|
+
bodyPreview: bodyPreview(flags.body ?? flags.bodyHtml),
|
|
36062
|
+
bodyHtmlFile: flags.bodyHtmlFile,
|
|
36063
|
+
attachmentCount: (flags.attach?.length ?? 0) + (flags.attachInline?.length ?? 0),
|
|
36064
|
+
attachments: attachmentNames(flags.attach, flags.attachInline)
|
|
35573
36065
|
});
|
|
35574
36066
|
if (confirmation) return confirmation;
|
|
35575
|
-
const args = ["gmail", kind, messageId];
|
|
36067
|
+
const args = ["gmail", kind, pos(messageId)];
|
|
35576
36068
|
appendReplyFlags(args, flags);
|
|
35577
36069
|
const result = await runOrDiagnose(args, { account });
|
|
35578
36070
|
if (!result.isError) logGmailDispatch(toolName, recipients, account);
|
|
35579
36071
|
return result;
|
|
35580
36072
|
}
|
|
36073
|
+
var GMAIL_RUN_BLOCKED_SETTINGS = /* @__PURE__ */ new Set(["forwarding", "autoforward", "filters", "delegates"]);
|
|
36074
|
+
function vetGmailRun(subcommand, args) {
|
|
36075
|
+
if (subcommand === "autoreply") {
|
|
36076
|
+
return "gog gmail autoreply sends mail and is not available through gog_gmail_run. Use gog_gmail_autoreply, which asks the user to confirm.";
|
|
36077
|
+
}
|
|
36078
|
+
if (GMAIL_RUN_BLOCKED_SETTINGS.has(subcommand)) {
|
|
36079
|
+
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.`;
|
|
36080
|
+
}
|
|
36081
|
+
if (subcommand === "settings") {
|
|
36082
|
+
const blocked = args.find((a) => GMAIL_RUN_BLOCKED_SETTINGS.has(a.toLowerCase()));
|
|
36083
|
+
if (blocked) {
|
|
36084
|
+
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.`;
|
|
36085
|
+
}
|
|
36086
|
+
}
|
|
36087
|
+
return void 0;
|
|
36088
|
+
}
|
|
35581
36089
|
function registerGmailTools(server) {
|
|
35582
36090
|
server.registerTool("gog_gmail_search", {
|
|
35583
36091
|
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.',
|
|
@@ -35593,7 +36101,7 @@ function registerGmailTools(server) {
|
|
|
35593
36101
|
account: accountParam
|
|
35594
36102
|
})
|
|
35595
36103
|
}, async ({ query, max, pageToken, page, maxPages, all, fromContact, account }) => {
|
|
35596
|
-
const args = ["gmail", "search", query];
|
|
36104
|
+
const args = ["gmail", "search", pos(query)];
|
|
35597
36105
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
35598
36106
|
if (all) args.push("--all");
|
|
35599
36107
|
if (fromContact) args.push(`--from-contact=${fromContact}`);
|
|
@@ -35624,13 +36132,13 @@ function registerGmailTools(server) {
|
|
|
35624
36132
|
account: accountParam
|
|
35625
36133
|
})
|
|
35626
36134
|
}, async ({ messageId, format, sanitizeContent, account }) => {
|
|
35627
|
-
const args = ["gmail", "get", messageId];
|
|
36135
|
+
const args = ["gmail", "get", pos(messageId)];
|
|
35628
36136
|
if (format) args.push(`--format=${format}`);
|
|
35629
36137
|
if (sanitizeContent) args.push("--sanitize-content");
|
|
35630
36138
|
return runOrDiagnose(args, { account });
|
|
35631
36139
|
});
|
|
35632
36140
|
server.registerTool("gog_gmail_send", {
|
|
35633
|
-
description: 'SENDS MAIL \u2014 asks the MCP host to show a confirmation prompt with the recipients, subject,
|
|
36141
|
+
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.',
|
|
35634
36142
|
annotations: { destructiveHint: true },
|
|
35635
36143
|
inputSchema: external_exports.object({
|
|
35636
36144
|
to: external_exports.string().describe("Recipient(s), comma-separated"),
|
|
@@ -35641,11 +36149,12 @@ function registerGmailTools(server) {
|
|
|
35641
36149
|
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.'),
|
|
35642
36150
|
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."),
|
|
35643
36151
|
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."),
|
|
35644
|
-
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.`),
|
|
36152
|
+
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).`),
|
|
35645
36153
|
attachInline: attachInlineParam,
|
|
35646
36154
|
account: accountParam
|
|
35647
36155
|
})
|
|
35648
36156
|
}, async ({ to, subject, body, cc, bcc, replyToMessageId, threadId, quote, attach, attachInline, account }, ctx) => {
|
|
36157
|
+
confinePaths(attach, "attach");
|
|
35649
36158
|
const args = ["gmail", "send", `--to=${to}`, `--subject=${subject}`, payloadArg("body", "body-file", body)];
|
|
35650
36159
|
if (cc) args.push(`--cc=${cc}`);
|
|
35651
36160
|
if (bcc) args.push(`--bcc=${bcc}`);
|
|
@@ -35664,9 +36173,11 @@ function registerGmailTools(server) {
|
|
|
35664
36173
|
recipientCount: recipients.length,
|
|
35665
36174
|
subject,
|
|
35666
36175
|
bodyLength: body.length,
|
|
36176
|
+
bodyPreview: bodyPreview(body),
|
|
35667
36177
|
threaded: Boolean(replyToMessageId || threadId),
|
|
35668
36178
|
quoting: Boolean(quote),
|
|
35669
|
-
attachmentCount: (attach?.length ?? 0) + (attachInline?.length ?? 0)
|
|
36179
|
+
attachmentCount: (attach?.length ?? 0) + (attachInline?.length ?? 0),
|
|
36180
|
+
attachments: attachmentNames(attach, attachInline)
|
|
35670
36181
|
});
|
|
35671
36182
|
if (confirmation) return confirmation;
|
|
35672
36183
|
const result = await runOrDiagnose(args, { account });
|
|
@@ -35674,22 +36185,32 @@ function registerGmailTools(server) {
|
|
|
35674
36185
|
return result;
|
|
35675
36186
|
});
|
|
35676
36187
|
server.registerTool("gog_gmail_reply", {
|
|
35677
|
-
description: 'SENDS MAIL \u2014 asks the MCP host to show a confirmation prompt with the resolved recipient, subject,
|
|
36188
|
+
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.',
|
|
35678
36189
|
annotations: { destructiveHint: true },
|
|
35679
36190
|
inputSchema: sendReplySchema
|
|
35680
36191
|
}, async ({ messageId, account, ...flags }, ctx) => {
|
|
35681
36192
|
assertNotBoth("bodyHtml", "bodyHtmlFile", flags.bodyHtml, flags.bodyHtmlFile);
|
|
36193
|
+
confineReplyPaths(flags);
|
|
35682
36194
|
return sendReply("reply", "gog_gmail_reply", messageId, account, flags, ctx);
|
|
35683
36195
|
});
|
|
35684
36196
|
server.registerTool("gog_gmail_reply_all", {
|
|
35685
|
-
description: 'SENDS MAIL \u2014 asks the MCP host to show a confirmation prompt with the resolved recipients, subject,
|
|
36197
|
+
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.',
|
|
35686
36198
|
annotations: { destructiveHint: true },
|
|
35687
36199
|
inputSchema: sendReplySchema
|
|
35688
36200
|
}, async ({ messageId, account, ...flags }, ctx) => {
|
|
35689
36201
|
assertNotBoth("bodyHtml", "bodyHtmlFile", flags.bodyHtml, flags.bodyHtmlFile);
|
|
36202
|
+
confineReplyPaths(flags);
|
|
35690
36203
|
return sendReply("reply-all", "gog_gmail_reply_all", messageId, account, flags, ctx);
|
|
35691
36204
|
});
|
|
35692
|
-
registerRunTool(server, {
|
|
36205
|
+
registerRunTool(server, {
|
|
36206
|
+
service: "gmail",
|
|
36207
|
+
examples: '"archive", "mark-read", "labels"',
|
|
36208
|
+
// gog's --gmail-no-send blocks send/reply/forward/drafts send and all of
|
|
36209
|
+
// their aliases at runtime (verified on gog 0.41.0). Sending goes through the
|
|
36210
|
+
// confirmed tools, never this escape hatch.
|
|
36211
|
+
gmailNoSend: true,
|
|
36212
|
+
vet: vetGmailRun
|
|
36213
|
+
});
|
|
35693
36214
|
}
|
|
35694
36215
|
|
|
35695
36216
|
// src/tools/sheets-a1.ts
|
|
@@ -35758,7 +36279,7 @@ function registerSheetsTools(server) {
|
|
|
35758
36279
|
account: accountParam
|
|
35759
36280
|
})
|
|
35760
36281
|
}, async ({ spreadsheetId, range, account }) => {
|
|
35761
|
-
return runOrDiagnose(["sheets", "get", spreadsheetId, range], { account });
|
|
36282
|
+
return runOrDiagnose(["sheets", "get", pos(spreadsheetId), pos(range)], { account });
|
|
35762
36283
|
});
|
|
35763
36284
|
server.registerTool("gog_sheets_update", {
|
|
35764
36285
|
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)").',
|
|
@@ -35778,7 +36299,7 @@ function registerSheetsTools(server) {
|
|
|
35778
36299
|
const readRange = expandAnchorRange(range, values.length, cols);
|
|
35779
36300
|
let existing;
|
|
35780
36301
|
try {
|
|
35781
|
-
existing = await run(["sheets", "get", spreadsheetId, readRange], { account });
|
|
36302
|
+
existing = await run(["sheets", "get", pos(spreadsheetId), pos(readRange)], { account });
|
|
35782
36303
|
} catch (err) {
|
|
35783
36304
|
return diagnose(err);
|
|
35784
36305
|
}
|
|
@@ -35790,7 +36311,7 @@ function registerSheetsTools(server) {
|
|
|
35790
36311
|
);
|
|
35791
36312
|
}
|
|
35792
36313
|
}
|
|
35793
|
-
const args = ["sheets", "update", spreadsheetId, range, `--values-json=${JSON.stringify(values)}`];
|
|
36314
|
+
const args = ["sheets", "update", pos(spreadsheetId), pos(range), `--values-json=${JSON.stringify(values)}`];
|
|
35794
36315
|
if (dry_run) args.push("--dry-run");
|
|
35795
36316
|
if (fail_on_formula_error) args.push("--fail-on-formula-error");
|
|
35796
36317
|
return runOrDiagnose(args, { account });
|
|
@@ -35806,7 +36327,7 @@ function registerSheetsTools(server) {
|
|
|
35806
36327
|
account: accountParam
|
|
35807
36328
|
})
|
|
35808
36329
|
}, async ({ spreadsheetId, range, values, account, dry_run }) => {
|
|
35809
|
-
const args = ["sheets", "append", spreadsheetId, range, `--values-json=${JSON.stringify(values)}`];
|
|
36330
|
+
const args = ["sheets", "append", pos(spreadsheetId), pos(range), `--values-json=${JSON.stringify(values)}`];
|
|
35810
36331
|
if (dry_run) args.push("--dry-run");
|
|
35811
36332
|
return runOrDiagnose(args, { account });
|
|
35812
36333
|
});
|
|
@@ -35820,7 +36341,7 @@ function registerSheetsTools(server) {
|
|
|
35820
36341
|
account: accountParam
|
|
35821
36342
|
})
|
|
35822
36343
|
}, async ({ spreadsheetId, range, account, dry_run }) => {
|
|
35823
|
-
const args = ["sheets", "clear", spreadsheetId, range];
|
|
36344
|
+
const args = ["sheets", "clear", pos(spreadsheetId), pos(range)];
|
|
35824
36345
|
if (dry_run) args.push("--dry-run");
|
|
35825
36346
|
return runOrDiagnose(args, { account });
|
|
35826
36347
|
});
|
|
@@ -35832,7 +36353,7 @@ function registerSheetsTools(server) {
|
|
|
35832
36353
|
account: accountParam
|
|
35833
36354
|
})
|
|
35834
36355
|
}, async ({ spreadsheetId, account }) => {
|
|
35835
|
-
return runOrDiagnose(["sheets", "metadata", spreadsheetId], { account });
|
|
36356
|
+
return runOrDiagnose(["sheets", "metadata", pos(spreadsheetId)], { account });
|
|
35836
36357
|
});
|
|
35837
36358
|
server.registerTool("gog_sheets_create", {
|
|
35838
36359
|
description: "Create a new Google Spreadsheet. Returns JSON with the new spreadsheetId and URL.",
|
|
@@ -35842,7 +36363,7 @@ function registerSheetsTools(server) {
|
|
|
35842
36363
|
account: accountParam
|
|
35843
36364
|
})
|
|
35844
36365
|
}, async ({ title, account }) => {
|
|
35845
|
-
return runOrDiagnose(["sheets", "create", title], { account });
|
|
36366
|
+
return runOrDiagnose(["sheets", "create", pos(title)], { account });
|
|
35846
36367
|
});
|
|
35847
36368
|
server.registerTool("gog_sheets_find_replace", {
|
|
35848
36369
|
description: "Find and replace text across an entire Google Spreadsheet.",
|
|
@@ -35854,7 +36375,7 @@ function registerSheetsTools(server) {
|
|
|
35854
36375
|
account: accountParam
|
|
35855
36376
|
})
|
|
35856
36377
|
}, async ({ spreadsheetId, find, replace, account }) => {
|
|
35857
|
-
return runOrDiagnose(["sheets", "find-replace", spreadsheetId, find, replace], { account });
|
|
36378
|
+
return runOrDiagnose(["sheets", "find-replace", pos(spreadsheetId), pos(find), pos(replace)], { account });
|
|
35858
36379
|
});
|
|
35859
36380
|
registerRunTool(server, { service: "sheets", examples: '"freeze", "add-tab", "rename-tab"' });
|
|
35860
36381
|
}
|
|
@@ -35862,8 +36383,9 @@ function registerSheetsTools(server) {
|
|
|
35862
36383
|
// src/tools/slides.ts
|
|
35863
36384
|
function registerSlidesTools(server) {
|
|
35864
36385
|
server.registerTool("gog_slides_export", {
|
|
35865
|
-
description: "Export a Google Slides presentation to a local file (pdf or pptx).",
|
|
35866
|
-
|
|
36386
|
+
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.",
|
|
36387
|
+
// Writes a file on the gog host and can overwrite one: not read-only.
|
|
36388
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
35867
36389
|
inputSchema: external_exports.object({
|
|
35868
36390
|
presentationId: external_exports.string().describe("Presentation ID"),
|
|
35869
36391
|
out: external_exports.string().optional().describe("Output file path"),
|
|
@@ -35872,7 +36394,8 @@ function registerSlidesTools(server) {
|
|
|
35872
36394
|
account: accountParam
|
|
35873
36395
|
})
|
|
35874
36396
|
}, async ({ presentationId, out, format, overwrite, account }) => {
|
|
35875
|
-
|
|
36397
|
+
if (out) confinePath(out, "out");
|
|
36398
|
+
const args = ["slides", "export", pos(presentationId)];
|
|
35876
36399
|
if (out) args.push(`--out=${out}`);
|
|
35877
36400
|
if (format) args.push(`--format=${format}`);
|
|
35878
36401
|
if (overwrite) args.push("--overwrite");
|
|
@@ -35886,7 +36409,7 @@ function registerSlidesTools(server) {
|
|
|
35886
36409
|
account: accountParam
|
|
35887
36410
|
})
|
|
35888
36411
|
}, async ({ presentationId, account }) => {
|
|
35889
|
-
return runOrDiagnose(["slides", "info", presentationId], { account });
|
|
36412
|
+
return runOrDiagnose(["slides", "info", pos(presentationId)], { account });
|
|
35890
36413
|
});
|
|
35891
36414
|
server.registerTool("gog_slides_create", {
|
|
35892
36415
|
description: "Create a new Google Slides presentation, optionally in a folder or copying from a template.",
|
|
@@ -35898,7 +36421,7 @@ function registerSlidesTools(server) {
|
|
|
35898
36421
|
account: accountParam
|
|
35899
36422
|
})
|
|
35900
36423
|
}, async ({ title, parent, template, account }) => {
|
|
35901
|
-
const args = ["slides", "create", title];
|
|
36424
|
+
const args = ["slides", "create", pos(title)];
|
|
35902
36425
|
if (parent) args.push(`--parent=${parent}`);
|
|
35903
36426
|
if (template) args.push(`--template=${template}`);
|
|
35904
36427
|
return runOrDiagnose(args, { account });
|
|
@@ -35913,7 +36436,7 @@ function registerSlidesTools(server) {
|
|
|
35913
36436
|
account: accountParam
|
|
35914
36437
|
})
|
|
35915
36438
|
}, async ({ presentationId, title, parent, account }) => {
|
|
35916
|
-
const args = ["slides", "copy", presentationId, title];
|
|
36439
|
+
const args = ["slides", "copy", pos(presentationId), pos(title)];
|
|
35917
36440
|
if (parent) args.push(`--parent=${parent}`);
|
|
35918
36441
|
return runOrDiagnose(args, { account });
|
|
35919
36442
|
});
|
|
@@ -35925,7 +36448,7 @@ function registerSlidesTools(server) {
|
|
|
35925
36448
|
account: accountParam
|
|
35926
36449
|
})
|
|
35927
36450
|
}, async ({ presentationId, account }) => {
|
|
35928
|
-
return runOrDiagnose(["slides", "list-slides", presentationId], { account });
|
|
36451
|
+
return runOrDiagnose(["slides", "list-slides", pos(presentationId)], { account });
|
|
35929
36452
|
});
|
|
35930
36453
|
server.registerTool("gog_slides_read_slide", {
|
|
35931
36454
|
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.",
|
|
@@ -35937,11 +36460,11 @@ function registerSlidesTools(server) {
|
|
|
35937
36460
|
account: accountParam
|
|
35938
36461
|
})
|
|
35939
36462
|
}, async ({ presentationId, slideId, detail, account }) => {
|
|
35940
|
-
const args = ["slides", "read-slide", presentationId, slideId];
|
|
36463
|
+
const args = ["slides", "read-slide", pos(presentationId), pos(slideId)];
|
|
35941
36464
|
if (detail) args.push("--detail");
|
|
35942
36465
|
return runOrDiagnose(args, { account });
|
|
35943
36466
|
});
|
|
35944
|
-
registerRunTool(server, { service: "slides", examples: '"
|
|
36467
|
+
registerRunTool(server, { service: "slides", examples: '"delete-slide", "update-notes"' });
|
|
35945
36468
|
}
|
|
35946
36469
|
|
|
35947
36470
|
// src/tools/tasks.ts
|
|
@@ -35963,7 +36486,7 @@ function registerTasksTools(server) {
|
|
|
35963
36486
|
account: accountParam
|
|
35964
36487
|
})
|
|
35965
36488
|
}, async ({ tasklistId, account }) => {
|
|
35966
|
-
return runOrDiagnose(["tasks", "list", tasklistId], { account });
|
|
36489
|
+
return runOrDiagnose(["tasks", "list", pos(tasklistId)], { account });
|
|
35967
36490
|
});
|
|
35968
36491
|
server.registerTool("gog_tasks_get", {
|
|
35969
36492
|
description: "Get a specific task by ID.",
|
|
@@ -35974,7 +36497,7 @@ function registerTasksTools(server) {
|
|
|
35974
36497
|
account: accountParam
|
|
35975
36498
|
})
|
|
35976
36499
|
}, async ({ tasklistId, taskId, account }) => {
|
|
35977
|
-
return runOrDiagnose(["tasks", "get", tasklistId, taskId], { account });
|
|
36500
|
+
return runOrDiagnose(["tasks", "get", pos(tasklistId), pos(taskId)], { account });
|
|
35978
36501
|
});
|
|
35979
36502
|
server.registerTool("gog_tasks_add", {
|
|
35980
36503
|
description: "Add a task to a Google Task list.",
|
|
@@ -35987,7 +36510,7 @@ function registerTasksTools(server) {
|
|
|
35987
36510
|
account: accountParam
|
|
35988
36511
|
})
|
|
35989
36512
|
}, async ({ tasklistId, title, notes, due, account }) => {
|
|
35990
|
-
const args = ["tasks", "add", tasklistId, `--title=${title}`];
|
|
36513
|
+
const args = ["tasks", "add", pos(tasklistId), `--title=${title}`];
|
|
35991
36514
|
if (notes) args.push(`--notes=${notes}`);
|
|
35992
36515
|
if (due) args.push(`--due=${due}`);
|
|
35993
36516
|
return runOrDiagnose(args, { account });
|
|
@@ -36001,7 +36524,7 @@ function registerTasksTools(server) {
|
|
|
36001
36524
|
account: accountParam
|
|
36002
36525
|
})
|
|
36003
36526
|
}, async ({ tasklistId, taskId, account }) => {
|
|
36004
|
-
return runOrDiagnose(["tasks", "done", tasklistId, taskId], { account });
|
|
36527
|
+
return runOrDiagnose(["tasks", "done", pos(tasklistId), pos(taskId)], { account });
|
|
36005
36528
|
});
|
|
36006
36529
|
server.registerTool("gog_tasks_delete", {
|
|
36007
36530
|
description: "Delete a task.",
|
|
@@ -36012,13 +36535,13 @@ function registerTasksTools(server) {
|
|
|
36012
36535
|
account: accountParam
|
|
36013
36536
|
})
|
|
36014
36537
|
}, async ({ tasklistId, taskId, account }) => {
|
|
36015
|
-
return runOrDiagnose(["tasks", "delete", tasklistId, taskId, "--force"], { account });
|
|
36538
|
+
return runOrDiagnose(["tasks", "delete", pos(tasklistId), pos(taskId), "--force"], { account });
|
|
36016
36539
|
});
|
|
36017
36540
|
registerRunTool(server, { service: "tasks", examples: '"update", "undo", "clear"' });
|
|
36018
36541
|
}
|
|
36019
36542
|
|
|
36020
36543
|
// src/server.ts
|
|
36021
|
-
var VERSION = true ? "4.
|
|
36544
|
+
var VERSION = true ? "4.3.0" : "0.0.0";
|
|
36022
36545
|
var BASE_TOOL_REGISTRARS = [
|
|
36023
36546
|
registerApiTools,
|
|
36024
36547
|
registerAppScriptTools,
|
|
@@ -36036,7 +36559,7 @@ var BASE_TOOL_REGISTRARS = [
|
|
|
36036
36559
|
];
|
|
36037
36560
|
|
|
36038
36561
|
// src/bootstrap-auth.ts
|
|
36039
|
-
import { join as
|
|
36562
|
+
import { join as join5 } from "node:path";
|
|
36040
36563
|
var AUTH_BOOTSTRAP_MARKER = "auth-bootstrap.sha256";
|
|
36041
36564
|
var VARS = ["GOG_CLIENT_ID", "GOG_CLIENT_SECRET", "GOG_REFRESH_TOKEN", "GOG_ACCOUNT"];
|
|
36042
36565
|
var log = (line) => {
|
|
@@ -36070,12 +36593,12 @@ async function bootstrapGogAuth(env = process.env, options = {}) {
|
|
|
36070
36593
|
const [clientId, clientSecret, refreshToken, account] = values;
|
|
36071
36594
|
const { spawner } = options;
|
|
36072
36595
|
try {
|
|
36073
|
-
const { createHash } = await import("node:crypto");
|
|
36596
|
+
const { createHash: createHash2 } = await import("node:crypto");
|
|
36074
36597
|
const { mkdir, readFile, writeFile, chmod } = await import("node:fs/promises");
|
|
36075
36598
|
const home = options.home ?? (await import("node:os")).homedir();
|
|
36076
|
-
const dir =
|
|
36077
|
-
const marker =
|
|
36078
|
-
const fingerprint =
|
|
36599
|
+
const dir = join5(home, ".gogcli-mcp");
|
|
36600
|
+
const marker = join5(dir, AUTH_BOOTSTRAP_MARKER);
|
|
36601
|
+
const fingerprint = createHash2("sha256").update(values.join("\0")).digest("hex");
|
|
36079
36602
|
const previous = await readFile(marker, "utf8").catch(() => void 0);
|
|
36080
36603
|
if (previous === fingerprint && await accountListed(account, spawner)) return "present";
|
|
36081
36604
|
await run(
|
|
@@ -36100,7 +36623,7 @@ async function bootstrapGogAuth(env = process.env, options = {}) {
|
|
|
36100
36623
|
}
|
|
36101
36624
|
|
|
36102
36625
|
// src/blob-urls.ts
|
|
36103
|
-
import { createHmac } from "node:crypto";
|
|
36626
|
+
import { createHmac as createHmac2 } from "node:crypto";
|
|
36104
36627
|
var BLOB_URL_MAX_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
36105
36628
|
var BLOB_URL_CEILING_MARGIN_MS = 60 * 1e3;
|
|
36106
36629
|
var BLOB_URL_DEFAULT_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -36113,7 +36636,7 @@ function writePayload(objectKey, contentType, exp) {
|
|
|
36113
36636
|
return `put\0${objectKey}\0${contentType}\0${exp}`;
|
|
36114
36637
|
}
|
|
36115
36638
|
function sign(signingKey, payload) {
|
|
36116
|
-
return
|
|
36639
|
+
return createHmac2("sha256", signingKey).update(payload, "utf8").digest("base64url");
|
|
36117
36640
|
}
|
|
36118
36641
|
function objectPathSegments(rest) {
|
|
36119
36642
|
const segments = rest.split("/");
|
|
@@ -36175,7 +36698,6 @@ function blobStoreFromEnv() {
|
|
|
36175
36698
|
}
|
|
36176
36699
|
|
|
36177
36700
|
// src/blob-upload.ts
|
|
36178
|
-
var ATTACHMENT_DOWNLOAD_ROOT = "/tmp/gog-attachments";
|
|
36179
36701
|
var MAX_BLOB_UPLOAD_BYTES = 100 * 1024 * 1024;
|
|
36180
36702
|
var BLOB_UPLOAD_TIMEOUT_MS = 12e4;
|
|
36181
36703
|
var ERROR_BODY_SNIPPET = 512;
|
|
@@ -36190,10 +36712,10 @@ var UploadError = class extends Error {
|
|
|
36190
36712
|
async function resolveConfined(root, requested) {
|
|
36191
36713
|
const path = await import("node:path");
|
|
36192
36714
|
const { realpath, stat } = await import("node:fs/promises");
|
|
36193
|
-
const
|
|
36715
|
+
const isInside3 = (base, candidate2) => candidate2 === base || candidate2.startsWith(base + path.sep);
|
|
36194
36716
|
const lexicalRoot = path.resolve(root);
|
|
36195
36717
|
const candidate = path.resolve(lexicalRoot, requested);
|
|
36196
|
-
if (!
|
|
36718
|
+
if (!isInside3(lexicalRoot, candidate)) {
|
|
36197
36719
|
throw new UploadError(`the attachment path must be inside ${lexicalRoot}`);
|
|
36198
36720
|
}
|
|
36199
36721
|
let realFile;
|
|
@@ -36204,7 +36726,7 @@ async function resolveConfined(root, requested) {
|
|
|
36204
36726
|
} catch (err) {
|
|
36205
36727
|
throw new UploadError(`the downloaded attachment could not be found: ${err.message}`);
|
|
36206
36728
|
}
|
|
36207
|
-
if (!
|
|
36729
|
+
if (!isInside3(realRoot, realFile)) {
|
|
36208
36730
|
throw new UploadError(`the attachment path must be inside ${realRoot}`);
|
|
36209
36731
|
}
|
|
36210
36732
|
const info = await stat(realFile);
|
|
@@ -36231,7 +36753,7 @@ async function putFile(target, file2, bytes, contentType, timeoutMs) {
|
|
|
36231
36753
|
const { createReadStream } = await import("node:fs");
|
|
36232
36754
|
const { pipeline } = await import("node:stream");
|
|
36233
36755
|
const transport = target.protocol === "https:" ? await import("node:https") : await import("node:http");
|
|
36234
|
-
return new Promise((
|
|
36756
|
+
return new Promise((resolve4, reject) => {
|
|
36235
36757
|
let settled = false;
|
|
36236
36758
|
const source = createReadStream(file2);
|
|
36237
36759
|
const req = transport.request(target, {
|
|
@@ -36261,7 +36783,7 @@ async function putFile(target, file2, bytes, contentType, timeoutMs) {
|
|
|
36261
36783
|
held += chunk.length;
|
|
36262
36784
|
});
|
|
36263
36785
|
res.on("error", fail);
|
|
36264
|
-
res.on("end", () => finish2(() =>
|
|
36786
|
+
res.on("end", () => finish2(() => resolve4({
|
|
36265
36787
|
status: res.statusCode,
|
|
36266
36788
|
body: Buffer.concat(chunks).toString().slice(0, ERROR_BODY_SNIPPET)
|
|
36267
36789
|
})));
|
|
@@ -36293,11 +36815,13 @@ async function uploadToBlobStore(request, options = {}) {
|
|
|
36293
36815
|
}
|
|
36294
36816
|
export {
|
|
36295
36817
|
ATTACHMENT_DOWNLOAD_ROOT,
|
|
36818
|
+
ATTACHMENT_TTL_MS,
|
|
36296
36819
|
AUTH_BOOTSTRAP_MARKER,
|
|
36297
36820
|
BASE_TOOL_REGISTRARS,
|
|
36298
36821
|
BLOB_UPLOAD_TIMEOUT_MS,
|
|
36299
36822
|
BLOB_URL_DEFAULT_TTL_MS,
|
|
36300
36823
|
BLOB_URL_MAX_TTL_MS,
|
|
36824
|
+
FILE_ROOTS_ENV,
|
|
36301
36825
|
INLINE_ATTACHMENT_LIMITS_TEXT,
|
|
36302
36826
|
MAX_BLOB_UPLOAD_BYTES,
|
|
36303
36827
|
MAX_INLINE_ATTACHMENT_BYTES,
|
|
@@ -36311,14 +36835,21 @@ export {
|
|
|
36311
36835
|
appendReplyFlags,
|
|
36312
36836
|
assertNotBoth,
|
|
36313
36837
|
attachInlineParam,
|
|
36838
|
+
attachmentNames,
|
|
36314
36839
|
authToolsFor,
|
|
36315
36840
|
blobStoreFromEnv,
|
|
36841
|
+
bodyPreview,
|
|
36316
36842
|
bootstrapGogAuth,
|
|
36843
|
+
confineAtFile,
|
|
36844
|
+
confinePath,
|
|
36845
|
+
confinePaths,
|
|
36317
36846
|
createBlobUrlMinter,
|
|
36847
|
+
defaultFileRoot,
|
|
36318
36848
|
diagnose,
|
|
36319
36849
|
errorText,
|
|
36320
36850
|
extractEmails,
|
|
36321
36851
|
fetchGmailPages,
|
|
36852
|
+
fileRoots,
|
|
36322
36853
|
finalizeGmailSearch,
|
|
36323
36854
|
ids,
|
|
36324
36855
|
inlineAttachmentArgs,
|
|
@@ -36331,6 +36862,8 @@ export {
|
|
|
36331
36862
|
pageTokenParam,
|
|
36332
36863
|
paginationParams,
|
|
36333
36864
|
payloadArg,
|
|
36865
|
+
pos,
|
|
36866
|
+
prepareDownloadRoot,
|
|
36334
36867
|
pushPaginationFlags,
|
|
36335
36868
|
registerApiTools,
|
|
36336
36869
|
registerAppScriptTools,
|
|
@@ -36346,6 +36879,7 @@ export {
|
|
|
36346
36879
|
registerSheetsTools,
|
|
36347
36880
|
registerSlidesTools,
|
|
36348
36881
|
registerTasksTools,
|
|
36882
|
+
removeDownload,
|
|
36349
36883
|
replySchema,
|
|
36350
36884
|
requireGmailDispatchConfirmation,
|
|
36351
36885
|
resolvePageToken,
|