gogcli-mcp 4.5.0 → 4.5.1
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 +537 -117
- package/dist/lib.js +576 -117
- package/manifest.json +7 -7
- package/package.json +2 -2
- package/server.json +2 -2
- package/src/dispatch-confirmation.ts +63 -3
- package/src/gmail-dispatch-guard.ts +19 -12
- package/src/lib.ts +6 -2
- package/src/tools/api.ts +74 -8
- package/src/tools/appscript.ts +63 -2
- package/src/tools/calendar.ts +83 -13
- package/src/tools/chat.ts +35 -4
- package/src/tools/classroom.ts +254 -25
- package/src/tools/docs.ts +7 -1
- package/src/tools/drive.ts +82 -10
- package/src/tools/gmail.ts +37 -1
- package/src/tools/sheets.ts +16 -1
- package/src/tools/utils.ts +10 -4
- package/tests/gmail-dispatch-guard.test.ts +24 -8
- package/tests/tools/api.test.ts +130 -5
- package/tests/tools/appscript.test.ts +6 -2
- package/tests/tools/dispatch-gates-more.test.ts +447 -0
- package/tests/tools/dispatch-gates.test.ts +52 -1
- package/tests/tools/docs.test.ts +5 -6
- package/tests/tools/drive.test.ts +2 -5
- package/tests/tools/run-vets-more.test.ts +269 -0
- package/tests/tools/run-vets.test.ts +158 -4
- package/tests/tools/sheets.test.ts +10 -11
- package/tests/tools/utils.test.ts +38 -41
package/dist/index.js
CHANGED
|
@@ -956,14 +956,14 @@ function promiseAllObject(promisesObj) {
|
|
|
956
956
|
}
|
|
957
957
|
function randomString(length = 10) {
|
|
958
958
|
const chars = "abcdefghijklmnopqrstuvwxyz";
|
|
959
|
-
let
|
|
959
|
+
let str2 = "";
|
|
960
960
|
for (let i = 0; i < length; i++) {
|
|
961
|
-
|
|
961
|
+
str2 += chars[Math.floor(Math.random() * chars.length)];
|
|
962
962
|
}
|
|
963
|
-
return
|
|
963
|
+
return str2;
|
|
964
964
|
}
|
|
965
|
-
function esc(
|
|
966
|
-
return JSON.stringify(
|
|
965
|
+
function esc(str2) {
|
|
966
|
+
return JSON.stringify(str2);
|
|
967
967
|
}
|
|
968
968
|
function slugify(input2) {
|
|
969
969
|
return input2.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -1077,8 +1077,8 @@ var primitiveTypes = /* @__PURE__ */ new Set([
|
|
|
1077
1077
|
"symbol",
|
|
1078
1078
|
"undefined"
|
|
1079
1079
|
]);
|
|
1080
|
-
function escapeRegex(
|
|
1081
|
-
return
|
|
1080
|
+
function escapeRegex(str2) {
|
|
1081
|
+
return str2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1082
1082
|
}
|
|
1083
1083
|
function clone(inst, def, params) {
|
|
1084
1084
|
const cl = new inst._zod.constr(def ?? inst._zod.def);
|
|
@@ -1332,13 +1332,13 @@ function getSizableOrigin(input2) {
|
|
|
1332
1332
|
return "unknown";
|
|
1333
1333
|
}
|
|
1334
1334
|
var highSurrogate = /[\uD800-\uDBFF]/;
|
|
1335
|
-
function codePointLength(
|
|
1336
|
-
const units =
|
|
1337
|
-
if (!highSurrogate.test(
|
|
1335
|
+
function codePointLength(str2) {
|
|
1336
|
+
const units = str2.length;
|
|
1337
|
+
if (!highSurrogate.test(str2))
|
|
1338
1338
|
return units;
|
|
1339
1339
|
let count = units;
|
|
1340
1340
|
for (let i = 0; i < units - 1; i++) {
|
|
1341
|
-
if ((
|
|
1341
|
+
if ((str2.charCodeAt(i) & 64512) === 55296 && (str2.charCodeAt(i + 1) & 64512) === 56320) {
|
|
1342
1342
|
count--;
|
|
1343
1343
|
i++;
|
|
1344
1344
|
}
|
|
@@ -25451,7 +25451,7 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
25451
25451
|
}
|
|
25452
25452
|
exports._ = _;
|
|
25453
25453
|
const plus = new _Code("+");
|
|
25454
|
-
function
|
|
25454
|
+
function str2(strs, ...args) {
|
|
25455
25455
|
const expr = [safeStringify(strs[0])];
|
|
25456
25456
|
let i = 0;
|
|
25457
25457
|
while (i < args.length) {
|
|
@@ -25462,7 +25462,7 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
25462
25462
|
optimize(expr);
|
|
25463
25463
|
return new _Code(expr);
|
|
25464
25464
|
}
|
|
25465
|
-
exports.str =
|
|
25465
|
+
exports.str = str2;
|
|
25466
25466
|
function addCodeArg(code, arg) {
|
|
25467
25467
|
if (arg instanceof _Code) code.push(...arg._items);
|
|
25468
25468
|
else if (arg instanceof Name) code.push(arg);
|
|
@@ -25495,7 +25495,7 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
25495
25495
|
if (typeof b == "string" && b[0] === '"' && !(a instanceof Name)) return `"${a}${b.slice(1)}`;
|
|
25496
25496
|
}
|
|
25497
25497
|
function strConcat(c1, c2) {
|
|
25498
|
-
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 :
|
|
25498
|
+
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str2`${c1}${c2}`;
|
|
25499
25499
|
}
|
|
25500
25500
|
exports.strConcat = strConcat;
|
|
25501
25501
|
function interpolate(x) {
|
|
@@ -26374,21 +26374,21 @@ var require_util = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
26374
26374
|
return (0, codegen_1._)`${topSchemaRef}${schemaPath}${(0, codegen_1.getProperty)(keyword)}`;
|
|
26375
26375
|
}
|
|
26376
26376
|
exports.schemaRefOrVal = schemaRefOrVal;
|
|
26377
|
-
function unescapeFragment(
|
|
26378
|
-
return unescapeJsonPointer(decodeURIComponent(
|
|
26377
|
+
function unescapeFragment(str2) {
|
|
26378
|
+
return unescapeJsonPointer(decodeURIComponent(str2));
|
|
26379
26379
|
}
|
|
26380
26380
|
exports.unescapeFragment = unescapeFragment;
|
|
26381
|
-
function escapeFragment(
|
|
26382
|
-
return encodeURIComponent(escapeJsonPointer(
|
|
26381
|
+
function escapeFragment(str2) {
|
|
26382
|
+
return encodeURIComponent(escapeJsonPointer(str2));
|
|
26383
26383
|
}
|
|
26384
26384
|
exports.escapeFragment = escapeFragment;
|
|
26385
|
-
function escapeJsonPointer(
|
|
26386
|
-
if (typeof
|
|
26387
|
-
return
|
|
26385
|
+
function escapeJsonPointer(str2) {
|
|
26386
|
+
if (typeof str2 == "number") return `${str2}`;
|
|
26387
|
+
return str2.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
26388
26388
|
}
|
|
26389
26389
|
exports.escapeJsonPointer = escapeJsonPointer;
|
|
26390
|
-
function unescapeJsonPointer(
|
|
26391
|
-
return
|
|
26390
|
+
function unescapeJsonPointer(str2) {
|
|
26391
|
+
return str2.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
26392
26392
|
}
|
|
26393
26393
|
exports.unescapeJsonPointer = unescapeJsonPointer;
|
|
26394
26394
|
function eachItem(xs, f) {
|
|
@@ -27274,8 +27274,8 @@ var require_json_schema_traverse = /* @__PURE__ */ __commonJSMin(((exports, modu
|
|
|
27274
27274
|
post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
|
|
27275
27275
|
}
|
|
27276
27276
|
}
|
|
27277
|
-
function escapeJsonPtr(
|
|
27278
|
-
return
|
|
27277
|
+
function escapeJsonPtr(str2) {
|
|
27278
|
+
return str2.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
27279
27279
|
}
|
|
27280
27280
|
}));
|
|
27281
27281
|
var require_resolve = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
@@ -28167,9 +28167,9 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
28167
28167
|
isIPV6: false
|
|
28168
28168
|
};
|
|
28169
28169
|
}
|
|
28170
|
-
function findToken(
|
|
28170
|
+
function findToken(str2, token) {
|
|
28171
28171
|
let ind = 0;
|
|
28172
|
-
for (let i = 0; i <
|
|
28172
|
+
for (let i = 0; i < str2.length; i++) if (str2[i] === token) ind++;
|
|
28173
28173
|
return ind;
|
|
28174
28174
|
}
|
|
28175
28175
|
function removeDotSegments(path) {
|
|
@@ -28684,7 +28684,7 @@ var require_core$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
28684
28684
|
const util_1 = require_util();
|
|
28685
28685
|
const $dataRefSchema = require_data();
|
|
28686
28686
|
const uri_1 = require_uri();
|
|
28687
|
-
const defaultRegExp = (
|
|
28687
|
+
const defaultRegExp = (str2, flags) => new RegExp(str2, flags);
|
|
28688
28688
|
defaultRegExp.code = "new RegExp";
|
|
28689
28689
|
const META_IGNORE_OPTIONS = [
|
|
28690
28690
|
"removeAdditional",
|
|
@@ -29390,16 +29390,16 @@ var require_multipleOf = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
29390
29390
|
}));
|
|
29391
29391
|
var require_ucs2length = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
29392
29392
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29393
|
-
function ucs2length(
|
|
29394
|
-
const len =
|
|
29393
|
+
function ucs2length(str2) {
|
|
29394
|
+
const len = str2.length;
|
|
29395
29395
|
let length = 0;
|
|
29396
29396
|
let pos2 = 0;
|
|
29397
29397
|
let value;
|
|
29398
29398
|
while (pos2 < len) {
|
|
29399
29399
|
length++;
|
|
29400
|
-
value =
|
|
29400
|
+
value = str2.charCodeAt(pos2++);
|
|
29401
29401
|
if (value >= 55296 && value <= 56319 && pos2 < len) {
|
|
29402
|
-
value =
|
|
29402
|
+
value = str2.charCodeAt(pos2);
|
|
29403
29403
|
if ((value & 64512) === 56320) pos2++;
|
|
29404
29404
|
}
|
|
29405
29405
|
}
|
|
@@ -32110,8 +32110,8 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
32110
32110
|
30,
|
|
32111
32111
|
31
|
|
32112
32112
|
];
|
|
32113
|
-
function date5(
|
|
32114
|
-
const matches = DATE.exec(
|
|
32113
|
+
function date5(str2) {
|
|
32114
|
+
const matches = DATE.exec(str2);
|
|
32115
32115
|
if (!matches) return false;
|
|
32116
32116
|
const year = +matches[1];
|
|
32117
32117
|
const month = +matches[2];
|
|
@@ -32126,8 +32126,8 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
32126
32126
|
}
|
|
32127
32127
|
const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
|
|
32128
32128
|
function getTime(strictTimeZone) {
|
|
32129
|
-
return function time3(
|
|
32130
|
-
const matches = TIME.exec(
|
|
32129
|
+
return function time3(str2) {
|
|
32130
|
+
const matches = TIME.exec(str2);
|
|
32131
32131
|
if (!matches) return false;
|
|
32132
32132
|
const hr = +matches[1];
|
|
32133
32133
|
const min = +matches[2];
|
|
@@ -32164,8 +32164,8 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
32164
32164
|
const DATE_TIME_SEPARATOR = /t|\s/i;
|
|
32165
32165
|
function getDateTime(strictTimeZone) {
|
|
32166
32166
|
const time3 = getTime(strictTimeZone);
|
|
32167
|
-
return function date_time(
|
|
32168
|
-
const dateTime =
|
|
32167
|
+
return function date_time(str2) {
|
|
32168
|
+
const dateTime = str2.split(DATE_TIME_SEPARATOR);
|
|
32169
32169
|
return dateTime.length === 2 && date5(dateTime[0]) && time3(dateTime[1]);
|
|
32170
32170
|
};
|
|
32171
32171
|
}
|
|
@@ -32186,13 +32186,13 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
32186
32186
|
}
|
|
32187
32187
|
const NOT_URI_FRAGMENT = /\/|:/;
|
|
32188
32188
|
const URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
|
|
32189
|
-
function uri(
|
|
32190
|
-
return NOT_URI_FRAGMENT.test(
|
|
32189
|
+
function uri(str2) {
|
|
32190
|
+
return NOT_URI_FRAGMENT.test(str2) && URI.test(str2);
|
|
32191
32191
|
}
|
|
32192
32192
|
const BYTE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gm;
|
|
32193
|
-
function byte(
|
|
32193
|
+
function byte(str2) {
|
|
32194
32194
|
BYTE.lastIndex = 0;
|
|
32195
|
-
return BYTE.test(
|
|
32195
|
+
return BYTE.test(str2);
|
|
32196
32196
|
}
|
|
32197
32197
|
const MIN_INT32 = -(2 ** 31);
|
|
32198
32198
|
const MAX_INT32 = 2 ** 31 - 1;
|
|
@@ -32206,10 +32206,10 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
32206
32206
|
return true;
|
|
32207
32207
|
}
|
|
32208
32208
|
const Z_ANCHOR = /[^\\]\\Z/;
|
|
32209
|
-
function regex(
|
|
32210
|
-
if (Z_ANCHOR.test(
|
|
32209
|
+
function regex(str2) {
|
|
32210
|
+
if (Z_ANCHOR.test(str2)) return false;
|
|
32211
32211
|
try {
|
|
32212
|
-
new RegExp(
|
|
32212
|
+
new RegExp(str2);
|
|
32213
32213
|
return true;
|
|
32214
32214
|
} catch (e) {
|
|
32215
32215
|
return false;
|
|
@@ -35971,6 +35971,7 @@ async function diagnose(err) {
|
|
|
35971
35971
|
const isAuthError = !isTransientError && AUTH_ERROR_PATTERN.test(errText);
|
|
35972
35972
|
const isGridLimitError = GRID_LIMIT_ERROR_PATTERN.test(errText);
|
|
35973
35973
|
const hint = isInvalidGrant ? INVALID_GRANT_HINT : isAuthError ? AUTH_HINT : isTransientError ? TRANSIENT_HINT : isGridLimitError ? GRID_LIMIT_HINT : "";
|
|
35974
|
+
if (!isAuthError && !isInvalidGrant) return errorResult(`${errText}${hint}`);
|
|
35974
35975
|
try {
|
|
35975
35976
|
const accounts = formatAccountList(await run(["auth", "list"]));
|
|
35976
35977
|
return errorResult(`${errText}
|
|
@@ -36115,12 +36116,53 @@ function attachmentPreview(details) {
|
|
|
36115
36116
|
var CONFIRM_SEND_INSTRUCTION = "Show this preview to the user verbatim and send only after they explicitly approve in chat. Then call again with confirmToken.";
|
|
36116
36117
|
var CONFIRM_ACTION_INSTRUCTION = CONFIRM_TOKEN_INSTRUCTION;
|
|
36117
36118
|
var CONFIRM_FALLBACK_DESCRIPTION = ' If the client cannot show that prompt (no MCP elicitation, e.g. claude.ai), a two-step flow applies instead: call WITHOUT confirmToken and nothing is sent or changed \u2014 the result has status "confirmation-required", the full preview and a confirmToken. Follow its instruction (by default: show the preview to the user verbatim and only after they explicitly approve it in chat, call again with the SAME arguments plus confirmToken). The tool re-reads what it would act on and refuses (DRAFT_CHANGED, with a fresh preview and token) if it changed; TOKEN_EXPIRED / TOKEN_REUSED / TOKEN_INVALID also do nothing. A server set to MCP_CONFIRM_MODE=refuse refuses instead.';
|
|
36119
|
+
function refusedInRun(what, via, does, instead) {
|
|
36120
|
+
return `${what} ${does} and is not available through ${via}. ${instead}`;
|
|
36121
|
+
}
|
|
36118
36122
|
function gatedElsewhere(what, via, does, tool) {
|
|
36119
|
-
return
|
|
36123
|
+
return refusedInRun(what, via, does, `Use ${tool}, which asks the user to confirm.`);
|
|
36120
36124
|
}
|
|
36121
36125
|
function hasCommandWord(args, words) {
|
|
36122
36126
|
return args.find((a) => words.has(a.toLowerCase()));
|
|
36123
36127
|
}
|
|
36128
|
+
function flagValue(args, name) {
|
|
36129
|
+
const flag = `--${name}`;
|
|
36130
|
+
for (let i = 0; i < args.length; i++) {
|
|
36131
|
+
const a = args[i];
|
|
36132
|
+
if (a === flag) return args[i + 1] ?? "";
|
|
36133
|
+
if (a.startsWith(`${flag}=`)) return a.slice(flag.length + 1);
|
|
36134
|
+
}
|
|
36135
|
+
return void 0;
|
|
36136
|
+
}
|
|
36137
|
+
var COMMENT_ADD_WORDS = /* @__PURE__ */ new Set(["create", "add", "new"]);
|
|
36138
|
+
var COMMENT_REPLY_WORDS = /* @__PURE__ */ new Set(["reply", "respond"]);
|
|
36139
|
+
function hasTrueFlag(args, flag) {
|
|
36140
|
+
const name = `--${flag}`;
|
|
36141
|
+
return args.some((a) => {
|
|
36142
|
+
const lower = a.toLowerCase();
|
|
36143
|
+
if (lower === name) return true;
|
|
36144
|
+
return lower.startsWith(`${name}=`) && lower.slice(name.length + 1) !== "false";
|
|
36145
|
+
});
|
|
36146
|
+
}
|
|
36147
|
+
function vetCommentsRun(service, subcommand, args) {
|
|
36148
|
+
if (subcommand.toLowerCase() !== "comments") return void 0;
|
|
36149
|
+
const reply = hasCommandWord(args, COMMENT_REPLY_WORDS);
|
|
36150
|
+
if (reply) {
|
|
36151
|
+
return gatedElsewhere(
|
|
36152
|
+
`gog ${service} comments ${reply.toLowerCase()}`,
|
|
36153
|
+
`gog_${service}_run`,
|
|
36154
|
+
"notifies the comment thread",
|
|
36155
|
+
`gog_${service}_comments_reply`
|
|
36156
|
+
);
|
|
36157
|
+
}
|
|
36158
|
+
const add = hasCommandWord(args, COMMENT_ADD_WORDS);
|
|
36159
|
+
return add ? gatedElsewhere(
|
|
36160
|
+
`gog ${service} comments ${add.toLowerCase()}`,
|
|
36161
|
+
`gog_${service}_run`,
|
|
36162
|
+
"notifies the file's owner and anyone it mentions",
|
|
36163
|
+
`gog_${service}_comments_add`
|
|
36164
|
+
) : void 0;
|
|
36165
|
+
}
|
|
36124
36166
|
async function requireDispatchConfirmation(ctx, options) {
|
|
36125
36167
|
const { action, fallback } = options;
|
|
36126
36168
|
const confirmation = {
|
|
@@ -36149,16 +36191,33 @@ function resultText(result) {
|
|
|
36149
36191
|
// src/tools/api.ts
|
|
36150
36192
|
var GMAIL_API_BLOCKED = /(?:\.send$|forwarding|filters\.create|filters\.update|delegates\.create)/i;
|
|
36151
36193
|
var DISPATCH_API_BLOCKED = {
|
|
36152
|
-
chat: [
|
|
36153
|
-
|
|
36194
|
+
chat: [
|
|
36195
|
+
{ method: /(?:^|\.)spaces\.messages\.create$/i, does: "posts a Chat message", tool: "gog_chat_messages_send / gog_chat_dm_send" },
|
|
36196
|
+
{ method: /(?:^|\.)spaces\.(?:setup|members\.create)$/i, does: "adds people to a space", tool: "gog_chat_spaces_create" }
|
|
36197
|
+
],
|
|
36198
|
+
drive: [
|
|
36199
|
+
{ method: /(?:^|\.)permissions\.(?:create|update)$/i, does: "grants access to a file", tool: "gog_drive_share" },
|
|
36200
|
+
{ method: /(?:^|\.)comments\.create$/i, does: "notifies the file's owner and anyone it mentions", tool: "gog_drive_comments_add / gog_docs_comments_add" },
|
|
36201
|
+
{ method: /(?:^|\.)replies\.create$/i, does: "notifies the comment thread", tool: "gog_drive_comments_reply / gog_docs_comments_reply" }
|
|
36202
|
+
],
|
|
36154
36203
|
classroom: [
|
|
36155
36204
|
{ method: /(?:^|\.)courses\.announcements\.create$/i, does: "posts to a class", tool: "gog_classroom_announcements_create" },
|
|
36156
|
-
{ method: /(?:^|\.)invitations\.create$/i, does: "invites someone to a class", tool: "gog_classroom_invitations_create" }
|
|
36205
|
+
{ method: /(?:^|\.)invitations\.create$/i, does: "invites someone to a class", tool: "gog_classroom_invitations_create" },
|
|
36206
|
+
{ method: /(?:^|\.)courses\.students\.create$/i, does: "adds someone to a class", tool: "gog_classroom_students_add" },
|
|
36207
|
+
{ method: /(?:^|\.)courses\.teachers\.create$/i, does: "gives someone teacher access to a class", tool: "gog_classroom_teachers_add" },
|
|
36208
|
+
{ method: /(?:^|\.)courses\.coursework\.create$/i, does: "posts coursework to a class", tool: "gog_classroom_coursework_create" },
|
|
36209
|
+
{ method: /(?:^|\.)studentsubmissions\.return$/i, does: "returns work to a student", tool: "gog_classroom_submissions_return" }
|
|
36157
36210
|
],
|
|
36158
36211
|
calendar: [
|
|
36159
36212
|
{ method: /(?:^|\.)events\.(?:insert|import|quickadd)$/i, does: "can put an event on guests' calendars", tool: "gog_calendar_create" },
|
|
36160
|
-
{ method: /(?:^|\.)events\.(?:update|patch)$/i, does: "can change what guests see", tool: "gog_calendar_update / gog_calendar_respond" }
|
|
36161
|
-
|
|
36213
|
+
{ method: /(?:^|\.)events\.(?:update|patch)$/i, does: "can change what guests see", tool: "gog_calendar_update / gog_calendar_respond" },
|
|
36214
|
+
{ method: /(?:^|\.)events\.move$/i, does: "can email every guest", tool: "gog_calendar_move" }
|
|
36215
|
+
],
|
|
36216
|
+
gmail: [
|
|
36217
|
+
{ method: /(?:^|\.)settings\.sendas\.create$/i, does: "makes Google email the address and adds a sending identity", tool: "gog_gmail_sendas_create" },
|
|
36218
|
+
{ method: /(?:^|\.)settings\.updatevacation$/i, does: "can turn on an auto-reply to every sender", tool: "gog_gmail_vacation_update" }
|
|
36219
|
+
],
|
|
36220
|
+
script: [{ method: /(?:^|\.)scripts\.run$/i, does: "executes code with this account's authority", tool: "gog_appscript_run_function" }]
|
|
36162
36221
|
};
|
|
36163
36222
|
function refusedApiCall(api, method) {
|
|
36164
36223
|
const name = api.trim().toLowerCase();
|
|
@@ -36169,6 +36228,14 @@ function refusedApiCall(api, method) {
|
|
|
36169
36228
|
const hit = (Object.hasOwn(DISPATCH_API_BLOCKED, name) ? DISPATCH_API_BLOCKED[name] : []).find((b) => b.method.test(m));
|
|
36170
36229
|
return hit ? gatedElsewhere(`${name} ${m}`, "gog_api_call", hit.does, hit.tool) : void 0;
|
|
36171
36230
|
}
|
|
36231
|
+
function previewJson(text) {
|
|
36232
|
+
if (text.length > BODY_PREVIEW_MAX) return bodyPreview(text);
|
|
36233
|
+
try {
|
|
36234
|
+
return JSON.parse(text);
|
|
36235
|
+
} catch {
|
|
36236
|
+
return text;
|
|
36237
|
+
}
|
|
36238
|
+
}
|
|
36172
36239
|
function registerApiTools(server) {
|
|
36173
36240
|
server.registerTool("gog_api_list", {
|
|
36174
36241
|
description: "List the Google Discovery APIs available for gog_api_call / gog_api_describe (name + version + title).",
|
|
@@ -36197,7 +36264,7 @@ function registerApiTools(server) {
|
|
|
36197
36264
|
return runOrDiagnose(args, { account });
|
|
36198
36265
|
});
|
|
36199
36266
|
server.registerTool("gog_api_call", {
|
|
36200
|
-
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
|
|
36267
|
+
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, and every write then asks the MCP host to show the user a confirmation prompt with the exact api/version/method/params/body before anything is sent; set dryRun=true to print the intended request without sending it (no prompt, no changes). Gmail send and forwarding methods (users.messages.send, users.drafts.send, forwarding/auto-forwarding, filters, delegates) are refused outright \u2014 use the dedicated gog_gmail_* tools, which ask the user to confirm. So are the other methods a dedicated tool asks about with a better preview: Chat spaces.messages.create, Drive permissions.create/update, Classroom courses.announcements.create and invitations.create, and Calendar events.insert/import/quickAdd/update/patch." + CONFIRM_FALLBACK_DESCRIPTION,
|
|
36201
36268
|
annotations: { destructiveHint: true },
|
|
36202
36269
|
inputSchema: external_exports.object({
|
|
36203
36270
|
api: external_exports.string().describe("Discovery API name (e.g. drive, gmail, calendar)"),
|
|
@@ -36206,11 +36273,12 @@ function registerApiTools(server) {
|
|
|
36206
36273
|
params: external_exports.string().optional().describe('Query/path parameters as a JSON object string (e.g. {"fileId":"abc","fields":"name"})'),
|
|
36207
36274
|
body: external_exports.string().optional().describe("Request body as a JSON string (for write methods)"),
|
|
36208
36275
|
scope: external_exports.string().optional().describe("Override the OAuth scope used for the call"),
|
|
36209
|
-
allowWrite: external_exports.boolean().optional().describe("Required to invoke a mutating method (POST/PUT/PATCH/DELETE). Without it, gog refuses write methods. Leave unset for read-only calls."),
|
|
36210
|
-
dryRun: external_exports.boolean().optional().describe("Print the intended request and exit without sending it (no changes made)"),
|
|
36211
|
-
account: accountParam
|
|
36276
|
+
allowWrite: external_exports.boolean().optional().describe("Required to invoke a mutating method (POST/PUT/PATCH/DELETE); the user is asked to confirm the exact request first. Without it, gog refuses write methods. Leave unset for read-only calls."),
|
|
36277
|
+
dryRun: external_exports.boolean().optional().describe("Print the intended request and exit without sending it (no changes made, no confirmation prompt)"),
|
|
36278
|
+
account: accountParam,
|
|
36279
|
+
confirmToken: confirmTokenParam
|
|
36212
36280
|
})
|
|
36213
|
-
}, async ({ api, version: version2, method, params, body, scope, allowWrite, dryRun, account }) => {
|
|
36281
|
+
}, async ({ api, version: version2, method, params, body, scope, allowWrite, dryRun, account, confirmToken }, ctx) => {
|
|
36214
36282
|
try {
|
|
36215
36283
|
assertSafeForwardedArgs([api, version2, method]);
|
|
36216
36284
|
const refusal = refusedApiCall(api, method);
|
|
@@ -36220,6 +36288,27 @@ function registerApiTools(server) {
|
|
|
36220
36288
|
} catch (err) {
|
|
36221
36289
|
return errorResult(errorText(err));
|
|
36222
36290
|
}
|
|
36291
|
+
if (allowWrite && !dryRun) {
|
|
36292
|
+
const request = { api, version: version2, method, params, body, scope };
|
|
36293
|
+
const view = { api, version: version2, method };
|
|
36294
|
+
if (params !== void 0) view.params = previewJson(params);
|
|
36295
|
+
if (body !== void 0) view.body = previewJson(body);
|
|
36296
|
+
if (scope !== void 0) view.scope = scope;
|
|
36297
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
36298
|
+
action: "api.call",
|
|
36299
|
+
message: `Review and confirm this raw Google API write (${api} ${version2} ${method}):`,
|
|
36300
|
+
confirmationLabel: "Confirm that this API method should be invoked with exactly these parameters.",
|
|
36301
|
+
details: view,
|
|
36302
|
+
unsupportedNote: "Set dryRun=true to see the request gog would send without sending it, or use the dedicated gog_* tool for this operation.",
|
|
36303
|
+
fallback: {
|
|
36304
|
+
tool: "gog_api_call",
|
|
36305
|
+
account,
|
|
36306
|
+
confirmToken,
|
|
36307
|
+
subject: () => ({ target: `${api}/${version2}/${method}`, payload: request, preview: view })
|
|
36308
|
+
}
|
|
36309
|
+
});
|
|
36310
|
+
if (confirmation) return confirmation;
|
|
36311
|
+
}
|
|
36223
36312
|
const args = ["api", "call", pos(api), pos(version2), pos(method)];
|
|
36224
36313
|
if (params) args.push(`--params=${params}`);
|
|
36225
36314
|
if (body) args.push(`--body=${body}`);
|
|
@@ -36232,6 +36321,22 @@ function registerApiTools(server) {
|
|
|
36232
36321
|
}
|
|
36233
36322
|
|
|
36234
36323
|
// src/tools/appscript.ts
|
|
36324
|
+
function vetAppScriptRun(subcommand, _args) {
|
|
36325
|
+
return subcommand.toLowerCase() === "run" ? gatedElsewhere("gog appscript run", "gog_appscript_run", "executes code with this account's authority", "gog_appscript_run_function") : void 0;
|
|
36326
|
+
}
|
|
36327
|
+
function projectSnapshot(raw, scriptId) {
|
|
36328
|
+
let project;
|
|
36329
|
+
try {
|
|
36330
|
+
project = JSON.parse(raw)?.project;
|
|
36331
|
+
} catch {
|
|
36332
|
+
project = void 0;
|
|
36333
|
+
}
|
|
36334
|
+
return {
|
|
36335
|
+
scriptId,
|
|
36336
|
+
...typeof project?.title === "string" ? { title: project.title } : {},
|
|
36337
|
+
...typeof project?.updateTime === "string" ? { updateTime: project.updateTime } : {}
|
|
36338
|
+
};
|
|
36339
|
+
}
|
|
36235
36340
|
function registerAppScriptTools(server) {
|
|
36236
36341
|
const scriptIdParam = external_exports.string().describe(
|
|
36237
36342
|
"Apps Script project ID \u2014 the long ID in script.google.com/\u2026/projects/<scriptId>/\u2026, NOT the Drive file ID of a container document"
|
|
@@ -36312,16 +36417,18 @@ function registerAppScriptTools(server) {
|
|
|
36312
36417
|
return runOrDiagnose(args, { account });
|
|
36313
36418
|
});
|
|
36314
36419
|
server.registerTool("gog_appscript_run_function", {
|
|
36315
|
-
description: "Execute a function in a deployed Apps Script project. TREAT THIS AS ARBITRARY CODE EXECUTION: the script runs with this Google account's authority and can send mail, edit Drive files or call external services, and the wrapper cannot tell a read from a write \u2014 read the code with gog_appscript_content first if you did not write it. Requires the project to be deployed as an API executable and to share the OAuth client with the calling credentials, otherwise Google refuses regardless of scopes. devMode runs the latest saved code instead of the deployed version, and only works if the account owns the script. This is NOT the escape hatch \u2014 gog_appscript_run is that." + apiEnableNote,
|
|
36420
|
+
description: "Execute a function in a deployed Apps Script project. TREAT THIS AS ARBITRARY CODE EXECUTION: the script runs with this Google account's authority and can send mail, edit Drive files or call external services, and the wrapper cannot tell a read from a write \u2014 read the code with gog_appscript_content first if you did not write it. Requires the project to be deployed as an API executable and to share the OAuth client with the calling credentials, otherwise Google refuses regardless of scopes. devMode runs the latest saved code instead of the deployed version, and only works if the account owns the script. This is NOT the escape hatch \u2014 gog_appscript_run is that. Because the wrapper cannot tell what the code will do, it reads the project and asks the MCP host to show the user a confirmation prompt with the project, the function and its arguments first; nothing runs unless they accept." + CONFIRM_FALLBACK_DESCRIPTION + apiEnableNote,
|
|
36316
36421
|
annotations: { destructiveHint: true },
|
|
36317
36422
|
inputSchema: external_exports.object({
|
|
36318
36423
|
scriptId: scriptIdParam,
|
|
36319
36424
|
functionName: external_exports.string().describe('Name of the function to call, e.g. "doWork"'),
|
|
36320
36425
|
params: external_exports.string().optional().describe(`Function parameters as a JSON ARRAY of positional arguments, e.g. '["a", 1]' \u2014 not an object`),
|
|
36321
36426
|
devMode: external_exports.boolean().optional().describe("Run the latest saved code rather than the deployed version (owner only)"),
|
|
36322
|
-
account: accountParam
|
|
36427
|
+
account: accountParam,
|
|
36428
|
+
confirmToken: confirmTokenParam
|
|
36323
36429
|
})
|
|
36324
|
-
}, async ({ scriptId, functionName, params, devMode, account }) => {
|
|
36430
|
+
}, async ({ scriptId, functionName, params, devMode, account, confirmToken }, ctx) => {
|
|
36431
|
+
let parsedParams;
|
|
36325
36432
|
if (params !== void 0) {
|
|
36326
36433
|
let parsed;
|
|
36327
36434
|
try {
|
|
@@ -36332,7 +36439,26 @@ function registerAppScriptTools(server) {
|
|
|
36332
36439
|
if (!Array.isArray(parsed)) {
|
|
36333
36440
|
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}`);
|
|
36334
36441
|
}
|
|
36442
|
+
parsedParams = parsed;
|
|
36335
36443
|
}
|
|
36444
|
+
const got = await runOrDiagnose(["appscript", "get", pos(scriptId)], { account });
|
|
36445
|
+
if (got.isError) return got;
|
|
36446
|
+
const { updateTime, ...project } = projectSnapshot(resultText(got), scriptId);
|
|
36447
|
+
const execution = { project, functionName, params: parsedParams, devMode: Boolean(devMode), runsAs: senderPreview(account) };
|
|
36448
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
36449
|
+
action: "appscript.run-function",
|
|
36450
|
+
message: "Review and confirm running this Apps Script function with the account's full authority:",
|
|
36451
|
+
confirmationLabel: "Confirm that this code should run now.",
|
|
36452
|
+
details: execution,
|
|
36453
|
+
unsupportedNote: "Ask the user to run it themselves from the Apps Script editor.",
|
|
36454
|
+
fallback: {
|
|
36455
|
+
tool: "gog_appscript_run_function",
|
|
36456
|
+
account,
|
|
36457
|
+
confirmToken,
|
|
36458
|
+
subject: () => ({ target: `${scriptId}/${functionName}`, revision: updateTime, payload: execution, preview: execution })
|
|
36459
|
+
}
|
|
36460
|
+
});
|
|
36461
|
+
if (confirmation) return confirmation;
|
|
36336
36462
|
const args = ["appscript", "run", pos(scriptId), pos(functionName)];
|
|
36337
36463
|
if (params !== void 0) args.push(`--params=${params}`);
|
|
36338
36464
|
if (devMode) args.push("--dev-mode");
|
|
@@ -36341,6 +36467,7 @@ function registerAppScriptTools(server) {
|
|
|
36341
36467
|
registerRunTool(server, {
|
|
36342
36468
|
service: "appscript",
|
|
36343
36469
|
examples: '"get", "content", "deployments"',
|
|
36470
|
+
vet: vetAppScriptRun,
|
|
36344
36471
|
note: "To execute a function, use gog_appscript_run_function \u2014 this tool is the generic escape hatch."
|
|
36345
36472
|
});
|
|
36346
36473
|
}
|
|
@@ -36480,21 +36607,47 @@ function registerAuthTools(server) {
|
|
|
36480
36607
|
}
|
|
36481
36608
|
|
|
36482
36609
|
// src/tools/calendar.ts
|
|
36483
|
-
var
|
|
36484
|
-
|
|
36485
|
-
|
|
36486
|
-
|
|
36487
|
-
|
|
36488
|
-
|
|
36489
|
-
|
|
36490
|
-
|
|
36491
|
-
|
|
36492
|
-
|
|
36610
|
+
var ASKS = (tool) => `Use ${tool}, which asks the user to confirm.`;
|
|
36611
|
+
var CALENDAR_REFUSED = {
|
|
36612
|
+
create: { does: "can change what guests see", instead: ASKS("gog_calendar_create") },
|
|
36613
|
+
add: { does: "can change what guests see", instead: ASKS("gog_calendar_create") },
|
|
36614
|
+
new: { does: "can change what guests see", instead: ASKS("gog_calendar_create") },
|
|
36615
|
+
update: { does: "can change what guests see", instead: ASKS("gog_calendar_update") },
|
|
36616
|
+
edit: { does: "can change what guests see", instead: ASKS("gog_calendar_update") },
|
|
36617
|
+
set: { does: "can change what guests see", instead: ASKS("gog_calendar_update") },
|
|
36618
|
+
respond: { does: "can change what guests see", instead: ASKS("gog_calendar_respond") },
|
|
36619
|
+
rsvp: { does: "can change what guests see", instead: ASKS("gog_calendar_respond") },
|
|
36620
|
+
reply: { does: "can change what guests see", instead: ASKS("gog_calendar_respond") },
|
|
36621
|
+
move: { does: "moves an event off guests' calendars (and can email them)", instead: ASKS("gog_calendar_move") },
|
|
36622
|
+
transfer: { does: "moves an event off guests' calendars (and can email them)", instead: ASKS("gog_calendar_move") },
|
|
36623
|
+
delete: { does: "removes an event from every guest's calendar (the whole series by default)", instead: ASKS("gog_calendar_delete") },
|
|
36624
|
+
del: { does: "removes an event from every guest's calendar (the whole series by default)", instead: ASKS("gog_calendar_delete") },
|
|
36625
|
+
rm: { does: "removes an event from every guest's calendar (the whole series by default)", instead: ASKS("gog_calendar_delete") },
|
|
36626
|
+
remove: { does: "removes an event from every guest's calendar (the whole series by default)", instead: ASKS("gog_calendar_delete") },
|
|
36627
|
+
"delete-calendar": { does: "deletes a calendar and every event on it", instead: ASKS("gog_calendar_delete_calendar") },
|
|
36628
|
+
"out-of-office": { does: "auto-declines invitations and notifies their organizers", instead: ASKS("gog_calendar_out_of_office") },
|
|
36629
|
+
ooo: { does: "auto-declines invitations and notifies their organizers", instead: ASKS("gog_calendar_out_of_office") }
|
|
36493
36630
|
};
|
|
36494
|
-
function vetCalendarRun(subcommand,
|
|
36631
|
+
function vetCalendarRun(subcommand, args) {
|
|
36495
36632
|
const sub = subcommand.toLowerCase();
|
|
36496
|
-
const
|
|
36497
|
-
|
|
36633
|
+
const what = `gog calendar ${sub}`;
|
|
36634
|
+
const via = "gog_calendar_run";
|
|
36635
|
+
if (Object.hasOwn(CALENDAR_REFUSED, sub)) {
|
|
36636
|
+
const { does, instead } = CALENDAR_REFUSED[sub];
|
|
36637
|
+
return refusedInRun(what, via, does, instead);
|
|
36638
|
+
}
|
|
36639
|
+
if (sub === "propose-time" && (flagValue(args, "decline") !== void 0 || flagValue(args, "comment") !== void 0)) {
|
|
36640
|
+
return gatedElsewhere(`${what} --decline`, via, "declines the event and notifies the organizer", "gog_calendar_respond");
|
|
36641
|
+
}
|
|
36642
|
+
if ((sub === "focus-time" || sub === "focus") && flagValue(args, "auto-decline")?.toLowerCase() !== "none") {
|
|
36643
|
+
return refusedInRun(
|
|
36644
|
+
what,
|
|
36645
|
+
via,
|
|
36646
|
+
"auto-declines invitations and notifies their organizers (gog defaults --auto-decline to all)",
|
|
36647
|
+
"Pass --auto-decline=none, or ask the user to create it from Google Calendar."
|
|
36648
|
+
);
|
|
36649
|
+
}
|
|
36650
|
+
return void 0;
|
|
36498
36651
|
}
|
|
36499
36652
|
function parseAttendees(value) {
|
|
36500
36653
|
if (!value) return [];
|
|
@@ -36511,15 +36664,17 @@ function eventSnapshot(raw) {
|
|
|
36511
36664
|
} catch {
|
|
36512
36665
|
event = void 0;
|
|
36513
36666
|
}
|
|
36514
|
-
const
|
|
36667
|
+
const str2 = (v) => typeof v === "string" ? v : void 0;
|
|
36515
36668
|
return {
|
|
36516
|
-
summary:
|
|
36669
|
+
summary: str2(event?.summary),
|
|
36517
36670
|
start: timeOf(event?.start),
|
|
36518
36671
|
end: timeOf(event?.end),
|
|
36519
|
-
organizer:
|
|
36672
|
+
organizer: str2(event?.organizer?.email),
|
|
36520
36673
|
// Guests are the OTHER people: not this account's own attendee row, not rooms.
|
|
36521
36674
|
guests: (Array.isArray(event?.attendees) ? event.attendees : []).filter((a) => a.self !== true && a.resource !== true && typeof a.email === "string").map((a) => a.email),
|
|
36522
|
-
|
|
36675
|
+
// A series, or one instance of one: gog's delete --scope defaults to all.
|
|
36676
|
+
...Array.isArray(event?.recurrence) && event.recurrence.length > 0 || typeof event?.recurringEventId === "string" ? { recurring: true } : {},
|
|
36677
|
+
etag: str2(event?.etag)
|
|
36523
36678
|
};
|
|
36524
36679
|
}
|
|
36525
36680
|
async function readEvent(calendarId, eventId, account) {
|
|
@@ -36740,14 +36895,35 @@ function registerCalendarTools(server) {
|
|
|
36740
36895
|
return runOrDiagnose(args, { account });
|
|
36741
36896
|
});
|
|
36742
36897
|
server.registerTool("gog_calendar_delete", {
|
|
36743
|
-
description: "Delete a calendar event.",
|
|
36898
|
+
description: "Delete a calendar event. It disappears from every guest's calendar too, and for a recurring event gog deletes the WHOLE series, so when the event has guests or recurs this reads it and asks the MCP host to show the user a confirmation prompt with the event as it stands; a guest-free, one-off event is deleted without asking." + CONFIRM_FALLBACK_DESCRIPTION,
|
|
36744
36899
|
annotations: { destructiveHint: true },
|
|
36745
36900
|
inputSchema: external_exports.object({
|
|
36746
36901
|
calendarId: external_exports.string().describe("Calendar ID"),
|
|
36747
36902
|
eventId: external_exports.string().describe("Event ID"),
|
|
36748
|
-
account: accountParam
|
|
36903
|
+
account: accountParam,
|
|
36904
|
+
confirmToken: confirmTokenParam
|
|
36749
36905
|
})
|
|
36750
|
-
}, async ({ calendarId, eventId, account }) => {
|
|
36906
|
+
}, async ({ calendarId, eventId, account, confirmToken }, ctx) => {
|
|
36907
|
+
const read = await readEvent(calendarId, eventId, account);
|
|
36908
|
+
if (read.error) return read.error;
|
|
36909
|
+
const { etag, ...event } = read.event;
|
|
36910
|
+
if (event.guests.length > 0 || event.recurring) {
|
|
36911
|
+
const view = { calendarId, eventId, event, scope: event.recurring ? "the whole recurring series" : "this event" };
|
|
36912
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
36913
|
+
action: "calendar.delete",
|
|
36914
|
+
message: event.recurring ? "Review and confirm deleting this ENTIRE recurring series:" : "Review and confirm deleting this event from every guest's calendar:",
|
|
36915
|
+
confirmationLabel: "Confirm that this event should be deleted now.",
|
|
36916
|
+
details: view,
|
|
36917
|
+
unsupportedNote: "Ask the user to delete it from Google Calendar.",
|
|
36918
|
+
fallback: {
|
|
36919
|
+
tool: "gog_calendar_delete",
|
|
36920
|
+
account,
|
|
36921
|
+
confirmToken,
|
|
36922
|
+
subject: () => ({ target: `${calendarId}/${eventId}`, revision: etag, payload: view, preview: view })
|
|
36923
|
+
}
|
|
36924
|
+
});
|
|
36925
|
+
if (confirmation) return confirmation;
|
|
36926
|
+
}
|
|
36751
36927
|
return runOrDiagnose(["calendar", "delete", pos(calendarId), pos(eventId), "--force"], { account });
|
|
36752
36928
|
});
|
|
36753
36929
|
server.registerTool("gog_calendar_respond", {
|
|
@@ -36869,8 +37045,14 @@ function inlineAttachmentArgs(flag, attachments, siblingArgs = []) {
|
|
|
36869
37045
|
|
|
36870
37046
|
// src/tools/chat.ts
|
|
36871
37047
|
var CHAT_SEND_WORDS = /* @__PURE__ */ new Set(["send", "create", "post"]);
|
|
37048
|
+
var CHAT_SPACE_CREATE_WORDS = /* @__PURE__ */ new Set(["create", "add", "new"]);
|
|
36872
37049
|
function vetChatRun(subcommand, args) {
|
|
36873
37050
|
const sub = subcommand.toLowerCase();
|
|
37051
|
+
if (sub === "spaces") {
|
|
37052
|
+
const word2 = hasCommandWord(args, CHAT_SPACE_CREATE_WORDS);
|
|
37053
|
+
const withMembers = args.some((a) => /^--members?(?:=|$)/i.test(a));
|
|
37054
|
+
return word2 && withMembers ? gatedElsewhere(`gog chat spaces ${word2.toLowerCase()} --member`, "gog_chat_run", "adds people to a space", "gog_chat_spaces_create") : void 0;
|
|
37055
|
+
}
|
|
36874
37056
|
if (sub !== "messages" && sub !== "dm") return void 0;
|
|
36875
37057
|
const word = hasCommandWord(args, CHAT_SEND_WORDS);
|
|
36876
37058
|
if (!word) return void 0;
|
|
@@ -36921,16 +37103,34 @@ function registerChatTools(server) {
|
|
|
36921
37103
|
return runOrDiagnose(args, { account });
|
|
36922
37104
|
});
|
|
36923
37105
|
server.registerTool("gog_chat_spaces_create", {
|
|
36924
|
-
description: "Create a named Chat space, optionally seeding its membership. Members are added immediately and are notified
|
|
37106
|
+
description: "Create a named Chat space, optionally seeding its membership. Members are added immediately and are notified, so with members this asks the MCP host to show the user a confirmation prompt with the space name and every member first; nothing is created unless they accept. A space with no members reaches nobody and is created without asking." + CONFIRM_FALLBACK_DESCRIPTION + workspaceOnlyNote,
|
|
36925
37107
|
annotations: { destructiveHint: true },
|
|
36926
37108
|
inputSchema: external_exports.object({
|
|
36927
37109
|
displayName: external_exports.string().describe("Display name for the new space"),
|
|
36928
37110
|
members: external_exports.array(external_exports.string()).optional().describe('Initial members, as email addresses or "users/..." resource names'),
|
|
36929
|
-
account: accountParam
|
|
37111
|
+
account: accountParam,
|
|
37112
|
+
confirmToken: confirmTokenParam
|
|
36930
37113
|
})
|
|
36931
|
-
}, async ({ displayName, members: members2, account }) => {
|
|
37114
|
+
}, async ({ displayName, members: members2, account, confirmToken }, ctx) => {
|
|
36932
37115
|
const args = ["chat", "spaces", "create", pos(displayName)];
|
|
36933
37116
|
if (members2) for (const member of members2) args.push(`--member=${member}`);
|
|
37117
|
+
if (members2?.length) {
|
|
37118
|
+
const space = { displayName, members: members2 };
|
|
37119
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
37120
|
+
action: "chat.space-create",
|
|
37121
|
+
message: "Review and confirm this Chat space \u2014 every member is added and notified:",
|
|
37122
|
+
confirmationLabel: "Confirm that these people should be added to a new space now.",
|
|
37123
|
+
details: space,
|
|
37124
|
+
unsupportedNote: "Create the space without members instead; the user can add people from Google Chat.",
|
|
37125
|
+
fallback: {
|
|
37126
|
+
tool: "gog_chat_spaces_create",
|
|
37127
|
+
account,
|
|
37128
|
+
confirmToken,
|
|
37129
|
+
subject: () => ({ target: displayName, payload: space, preview: space })
|
|
37130
|
+
}
|
|
37131
|
+
});
|
|
37132
|
+
if (confirmation) return confirmation;
|
|
37133
|
+
}
|
|
36934
37134
|
return runOrDiagnose(args, { account });
|
|
36935
37135
|
});
|
|
36936
37136
|
server.registerTool("gog_chat_threads_list", {
|
|
@@ -37124,20 +37324,85 @@ function registerChatTools(server) {
|
|
|
37124
37324
|
|
|
37125
37325
|
// src/tools/classroom.ts
|
|
37126
37326
|
var CLASSROOM_CREATE_WORDS = /* @__PURE__ */ new Set(["create", "add", "new"]);
|
|
37127
|
-
var
|
|
37128
|
-
|
|
37129
|
-
|
|
37130
|
-
|
|
37131
|
-
|
|
37132
|
-
|
|
37133
|
-
|
|
37327
|
+
var CLASSROOM_DELETE_WORDS = /* @__PURE__ */ new Set(["delete", "rm", "del", "remove"]);
|
|
37328
|
+
var CLASSROOM_UPDATE_WORDS = /* @__PURE__ */ new Set(["update", "edit", "set"]);
|
|
37329
|
+
var CLASSROOM_RETURN_WORDS = /* @__PURE__ */ new Set(["return", "send"]);
|
|
37330
|
+
var CLASSROOM_ASSIGNEE_WORDS = /* @__PURE__ */ new Set(["assignees", "assign"]);
|
|
37331
|
+
var CLASSROOM_GROUPS = {
|
|
37332
|
+
courses: "courses",
|
|
37333
|
+
course: "courses",
|
|
37334
|
+
announcements: "announcements",
|
|
37335
|
+
announcement: "announcements",
|
|
37336
|
+
ann: "announcements",
|
|
37337
|
+
invitations: "invitations",
|
|
37338
|
+
invitation: "invitations",
|
|
37339
|
+
invites: "invitations",
|
|
37340
|
+
coursework: "coursework",
|
|
37341
|
+
work: "coursework",
|
|
37342
|
+
materials: "materials",
|
|
37343
|
+
material: "materials",
|
|
37344
|
+
students: "students",
|
|
37345
|
+
student: "students",
|
|
37346
|
+
teachers: "teachers",
|
|
37347
|
+
teacher: "teachers",
|
|
37348
|
+
submissions: "submissions",
|
|
37349
|
+
submission: "submissions",
|
|
37350
|
+
"guardian-invitations": "guardian-invitations",
|
|
37351
|
+
"guardian-invites": "guardian-invitations"
|
|
37134
37352
|
};
|
|
37353
|
+
function publishesToStudents(args) {
|
|
37354
|
+
return flagValue(args, "state")?.toUpperCase() === "PUBLISHED" || flagValue(args, "scheduled") !== void 0;
|
|
37355
|
+
}
|
|
37356
|
+
function createsVisibleWork(args) {
|
|
37357
|
+
return flagValue(args, "state")?.toUpperCase() !== "DRAFT" || flagValue(args, "scheduled") !== void 0;
|
|
37358
|
+
}
|
|
37359
|
+
function widensAssignees(args) {
|
|
37360
|
+
return flagValue(args, "mode")?.toUpperCase() === "ALL_STUDENTS" || flagValue(args, "add-student") !== void 0;
|
|
37361
|
+
}
|
|
37135
37362
|
function vetClassroomRun(subcommand, args) {
|
|
37136
37363
|
const sub = subcommand.toLowerCase();
|
|
37137
|
-
|
|
37138
|
-
|
|
37139
|
-
const
|
|
37140
|
-
|
|
37364
|
+
if (!Object.hasOwn(CLASSROOM_GROUPS, sub)) return void 0;
|
|
37365
|
+
const group = CLASSROOM_GROUPS[sub];
|
|
37366
|
+
const via = "gog_classroom_run";
|
|
37367
|
+
const what = (word) => `gog classroom ${sub} ${word.toLowerCase()}`;
|
|
37368
|
+
const create = hasCommandWord(args, CLASSROOM_CREATE_WORDS);
|
|
37369
|
+
const update = hasCommandWord(args, CLASSROOM_UPDATE_WORDS);
|
|
37370
|
+
const remove = hasCommandWord(args, CLASSROOM_DELETE_WORDS);
|
|
37371
|
+
const widenedAssignees = () => {
|
|
37372
|
+
const word = hasCommandWord(args, CLASSROOM_ASSIGNEE_WORDS);
|
|
37373
|
+
return word && widensAssignees(args) ? refusedInRun(what(word), via, "shows it to more students", "Ask the user to change who it is assigned to from Classroom.") : void 0;
|
|
37374
|
+
};
|
|
37375
|
+
switch (group) {
|
|
37376
|
+
case "courses":
|
|
37377
|
+
return remove ? gatedElsewhere(what(remove), via, "deletes a course", "gog_classroom_courses_delete") : void 0;
|
|
37378
|
+
case "announcements":
|
|
37379
|
+
if (create) return gatedElsewhere(what(create), via, "posts to a class", "gog_classroom_announcements_create");
|
|
37380
|
+
if (update && publishesToStudents(args)) return gatedElsewhere(what(update), via, "publishes an announcement to a class", "gog_classroom_announcements_update");
|
|
37381
|
+
return widenedAssignees();
|
|
37382
|
+
case "invitations":
|
|
37383
|
+
return create ? gatedElsewhere(what(create), via, "invites someone to a class", "gog_classroom_invitations_create") : void 0;
|
|
37384
|
+
case "coursework":
|
|
37385
|
+
if (create && createsVisibleWork(args)) return gatedElsewhere(what(create), via, "posts coursework to a class", "gog_classroom_coursework_create");
|
|
37386
|
+
if (remove) return gatedElsewhere(what(remove), via, "deletes coursework and every submission to it", "gog_classroom_coursework_delete");
|
|
37387
|
+
if (update && publishesToStudents(args)) return gatedElsewhere(what(update), via, "publishes work to students", "gog_classroom_coursework_update");
|
|
37388
|
+
return widenedAssignees();
|
|
37389
|
+
case "materials":
|
|
37390
|
+
if (create && createsVisibleWork(args)) {
|
|
37391
|
+
return refusedInRun(what(create), via, "publishes material to students", "Create it with --state=DRAFT and no --scheduled; ask the user to publish it from Classroom.");
|
|
37392
|
+
}
|
|
37393
|
+
if (update && publishesToStudents(args)) return refusedInRun(what(update), via, "publishes material to students", "Ask the user to publish it from Classroom.");
|
|
37394
|
+
return void 0;
|
|
37395
|
+
case "students":
|
|
37396
|
+
return create ? gatedElsewhere(what(create), via, "enrols someone in a class", "gog_classroom_students_add") : void 0;
|
|
37397
|
+
case "teachers":
|
|
37398
|
+
return create ? gatedElsewhere(what(create), via, "gives someone a teacher's access to a class and its roster", "gog_classroom_teachers_add") : void 0;
|
|
37399
|
+
case "submissions": {
|
|
37400
|
+
const word = hasCommandWord(args, CLASSROOM_RETURN_WORDS);
|
|
37401
|
+
return word ? gatedElsewhere(what(word), via, "returns work to a student, who is notified", "gog_classroom_submissions_return") : void 0;
|
|
37402
|
+
}
|
|
37403
|
+
case "guardian-invitations":
|
|
37404
|
+
return create ? refusedInRun(what(create), via, "emails a guardian invitation", "Ask the user to invite the guardian from Classroom.") : void 0;
|
|
37405
|
+
}
|
|
37141
37406
|
}
|
|
37142
37407
|
async function readCourse(courseId, account, runner = runOrDiagnose) {
|
|
37143
37408
|
const got = await runner(["classroom", "courses", "get", pos(courseId)], { account });
|
|
@@ -37156,6 +37421,52 @@ async function readCourse(courseId, account, runner = runOrDiagnose) {
|
|
|
37156
37421
|
}
|
|
37157
37422
|
};
|
|
37158
37423
|
}
|
|
37424
|
+
function nested(raw, key) {
|
|
37425
|
+
try {
|
|
37426
|
+
const parsed = JSON.parse(raw);
|
|
37427
|
+
const inner = parsed?.[key];
|
|
37428
|
+
return inner && typeof inner === "object" ? inner : void 0;
|
|
37429
|
+
} catch {
|
|
37430
|
+
return void 0;
|
|
37431
|
+
}
|
|
37432
|
+
}
|
|
37433
|
+
var str = (v) => typeof v === "string" ? v : void 0;
|
|
37434
|
+
var num = (v) => typeof v === "number" ? v : void 0;
|
|
37435
|
+
async function readCoursework(courseId, courseworkId, account, runner = runOrDiagnose) {
|
|
37436
|
+
const got = await runner(["classroom", "coursework", "get", pos(courseId), pos(courseworkId)], { account });
|
|
37437
|
+
if (got.isError) return { error: got };
|
|
37438
|
+
const work = nested(resultText(got), "coursework");
|
|
37439
|
+
const title = str(work?.title);
|
|
37440
|
+
return { coursework: { id: courseworkId, ...title !== void 0 ? { title } : {} } };
|
|
37441
|
+
}
|
|
37442
|
+
async function readSubmission(courseId, courseworkId, submissionId, account, runner = runOrDiagnose) {
|
|
37443
|
+
const got = await runner(["classroom", "submissions", "get", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
37444
|
+
if (got.isError) return { error: got };
|
|
37445
|
+
const sub = nested(resultText(got), "submission");
|
|
37446
|
+
const fields = {
|
|
37447
|
+
student: str(sub?.userId),
|
|
37448
|
+
state: str(sub?.state),
|
|
37449
|
+
draftGrade: num(sub?.draftGrade),
|
|
37450
|
+
assignedGrade: num(sub?.assignedGrade)
|
|
37451
|
+
};
|
|
37452
|
+
const updateTime = str(sub?.updateTime);
|
|
37453
|
+
return {
|
|
37454
|
+
submission: {
|
|
37455
|
+
id: submissionId,
|
|
37456
|
+
...Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== void 0))
|
|
37457
|
+
},
|
|
37458
|
+
...updateTime !== void 0 ? { updateTime } : {}
|
|
37459
|
+
};
|
|
37460
|
+
}
|
|
37461
|
+
async function studentLabel(courseId, userId, account, runner = runOrDiagnose) {
|
|
37462
|
+
const got = await runner(["classroom", "students", "get", pos(courseId), pos(userId)], { account });
|
|
37463
|
+
if (got.isError) return userId;
|
|
37464
|
+
const profile = nested(resultText(got), "student")?.profile;
|
|
37465
|
+
const name = str(profile?.name?.fullName);
|
|
37466
|
+
const email3 = str(profile?.emailAddress);
|
|
37467
|
+
if (name && email3) return `${name} <${email3}>`;
|
|
37468
|
+
return name ?? email3 ?? userId;
|
|
37469
|
+
}
|
|
37159
37470
|
function registerClassroomTools(server) {
|
|
37160
37471
|
server.registerTool("gog_classroom_courses_list", {
|
|
37161
37472
|
description: "List Google Classroom courses.",
|
|
@@ -37368,15 +37679,38 @@ function registerClassroomTools(server) {
|
|
|
37368
37679
|
return runOrDiagnose(args, { account });
|
|
37369
37680
|
});
|
|
37370
37681
|
server.registerTool("gog_classroom_submissions_return", {
|
|
37371
|
-
description: "Return a graded submission to the student.",
|
|
37682
|
+
description: "Return a graded submission to the student, who is notified and sees the grade. Reads the course, the coursework and the submission and asks the MCP host to show the user a confirmation prompt with the class, the assignment, the student and the grade being returned; nothing is returned unless they accept." + CONFIRM_FALLBACK_DESCRIPTION,
|
|
37372
37683
|
annotations: { destructiveHint: true },
|
|
37373
37684
|
inputSchema: external_exports.object({
|
|
37374
37685
|
courseId: external_exports.string().describe("Course ID"),
|
|
37375
37686
|
courseworkId: external_exports.string().describe("Coursework ID"),
|
|
37376
37687
|
submissionId: external_exports.string().describe("Submission ID"),
|
|
37377
|
-
account: accountParam
|
|
37688
|
+
account: accountParam,
|
|
37689
|
+
confirmToken: confirmTokenParam
|
|
37378
37690
|
})
|
|
37379
|
-
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
37691
|
+
}, async ({ courseId, courseworkId, submissionId, account, confirmToken }, ctx) => {
|
|
37692
|
+
const course = await readCourse(courseId, account);
|
|
37693
|
+
if (course.error) return course.error;
|
|
37694
|
+
const work = await readCoursework(courseId, courseworkId, account);
|
|
37695
|
+
if (work.error) return work.error;
|
|
37696
|
+
const sub = await readSubmission(courseId, courseworkId, submissionId, account);
|
|
37697
|
+
if (sub.error) return sub.error;
|
|
37698
|
+
const submission = sub.submission.student ? { ...sub.submission, student: await studentLabel(courseId, sub.submission.student, account) } : sub.submission;
|
|
37699
|
+
const view = { course: course.course, coursework: work.coursework, submission };
|
|
37700
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
37701
|
+
action: "classroom.submission-return",
|
|
37702
|
+
message: "Review and confirm returning this work \u2014 the student is notified and sees the grade:",
|
|
37703
|
+
confirmationLabel: "Confirm that this submission should be returned to the student now.",
|
|
37704
|
+
details: view,
|
|
37705
|
+
unsupportedNote: "Ask the user to return it from Classroom.",
|
|
37706
|
+
fallback: {
|
|
37707
|
+
tool: "gog_classroom_submissions_return",
|
|
37708
|
+
account,
|
|
37709
|
+
confirmToken,
|
|
37710
|
+
subject: () => ({ target: `${courseId}/${courseworkId}/${submissionId}`, revision: sub.updateTime, payload: view, preview: view })
|
|
37711
|
+
}
|
|
37712
|
+
});
|
|
37713
|
+
if (confirmation) return confirmation;
|
|
37380
37714
|
return runOrDiagnose(["classroom", "submissions", "return", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
37381
37715
|
});
|
|
37382
37716
|
server.registerTool("gog_classroom_submissions_turn_in", {
|
|
@@ -37438,13 +37772,13 @@ function registerClassroomTools(server) {
|
|
|
37438
37772
|
return runOrDiagnose(["classroom", "announcements", "get", pos(courseId), pos(announcementId)], { account });
|
|
37439
37773
|
});
|
|
37440
37774
|
server.registerTool("gog_classroom_announcements_create", {
|
|
37441
|
-
description: "Create an announcement in a Google Classroom course. Unless state is DRAFT (which students cannot see), this reads the course and asks the MCP host to show the user a confirmation prompt with the class, the full text and when it publishes; nothing is posted unless they accept. To stage one without asking, pass state DRAFT." + CONFIRM_FALLBACK_DESCRIPTION,
|
|
37775
|
+
description: "Create an announcement in a Google Classroom course. Unless state is DRAFT with no scheduled time (which students cannot see; a scheduled draft publishes itself), this reads the course and asks the MCP host to show the user a confirmation prompt with the class, the full text and when it publishes; nothing is posted unless they accept. To stage one without asking, pass state DRAFT and no scheduled time." + CONFIRM_FALLBACK_DESCRIPTION,
|
|
37442
37776
|
annotations: { destructiveHint: true },
|
|
37443
37777
|
inputSchema: external_exports.object({
|
|
37444
37778
|
courseId: external_exports.string().describe("Course ID"),
|
|
37445
37779
|
text: external_exports.string().describe("Announcement text"),
|
|
37446
|
-
state: external_exports.enum(["PUBLISHED", "DRAFT"]).optional().describe("State (DRAFT is visible only to teachers and needs no confirmation)"),
|
|
37447
|
-
scheduled: external_exports.string().optional().describe("Scheduled publish time"),
|
|
37780
|
+
state: external_exports.enum(["PUBLISHED", "DRAFT"]).optional().describe("State (an unscheduled DRAFT is visible only to teachers and needs no confirmation)"),
|
|
37781
|
+
scheduled: external_exports.string().optional().describe("Scheduled publish time (asks the user to confirm, even for a DRAFT)"),
|
|
37448
37782
|
account: accountParam,
|
|
37449
37783
|
confirmToken: confirmTokenParam
|
|
37450
37784
|
})
|
|
@@ -37452,7 +37786,7 @@ function registerClassroomTools(server) {
|
|
|
37452
37786
|
const args = ["classroom", "announcements", "create", pos(courseId), `--text=${text}`];
|
|
37453
37787
|
if (state) args.push(`--state=${state}`);
|
|
37454
37788
|
if (scheduled) args.push(`--scheduled=${scheduled}`);
|
|
37455
|
-
if (state !== "DRAFT") {
|
|
37789
|
+
if (state !== "DRAFT" || scheduled) {
|
|
37456
37790
|
const read = await readCourse(courseId, account);
|
|
37457
37791
|
if (read.error) return read.error;
|
|
37458
37792
|
const publishes = scheduled ? `at ${scheduled}` : "immediately";
|
|
@@ -37624,6 +37958,9 @@ function registerContactsTools(server) {
|
|
|
37624
37958
|
}
|
|
37625
37959
|
|
|
37626
37960
|
// src/tools/docs.ts
|
|
37961
|
+
function vetDocsRun(subcommand, args) {
|
|
37962
|
+
return vetCommentsRun("docs", subcommand, args);
|
|
37963
|
+
}
|
|
37627
37964
|
function registerDocsTools(server) {
|
|
37628
37965
|
server.registerTool("gog_docs_info", {
|
|
37629
37966
|
description: "Get Google Doc metadata: title, ID, and other properties.",
|
|
@@ -37720,12 +38057,29 @@ function registerDocsTools(server) {
|
|
|
37720
38057
|
}, async ({ docId, account }) => {
|
|
37721
38058
|
return runOrDiagnose(["docs", "structure", pos(docId)], { account });
|
|
37722
38059
|
});
|
|
37723
|
-
registerRunTool(server, { service: "docs", examples: '"copy", "clear", "insert", "sed", "export"' });
|
|
38060
|
+
registerRunTool(server, { service: "docs", examples: '"copy", "clear", "insert", "sed", "export"', vet: vetDocsRun });
|
|
37724
38061
|
}
|
|
37725
38062
|
|
|
37726
38063
|
// src/tools/drive.ts
|
|
37727
|
-
|
|
37728
|
-
|
|
38064
|
+
var DRIVE_DELETE_WORDS = /* @__PURE__ */ new Set(["delete", "rm", "del"]);
|
|
38065
|
+
function vetDriveRun(subcommand, args) {
|
|
38066
|
+
const sub = subcommand.toLowerCase();
|
|
38067
|
+
if (sub === "share") return gatedElsewhere("gog drive share", "gog_drive_run", "grants access to a file", "gog_drive_share");
|
|
38068
|
+
if (DRIVE_DELETE_WORDS.has(sub) && hasTrueFlag(args, "permanent")) {
|
|
38069
|
+
return gatedElsewhere(`gog drive ${sub} --permanent`, "gog_drive_run", "deletes a file for good, bypassing the trash", "gog_drive_delete");
|
|
38070
|
+
}
|
|
38071
|
+
if (sub === "bulk") {
|
|
38072
|
+
return gatedElsewhere(
|
|
38073
|
+
"gog drive bulk",
|
|
38074
|
+
"gog_drive_run",
|
|
38075
|
+
"rewrites sharing permissions across every matching file in a tree",
|
|
38076
|
+
"gog_drive_share (one file at a time)"
|
|
38077
|
+
);
|
|
38078
|
+
}
|
|
38079
|
+
if (sub === "unshare") {
|
|
38080
|
+
return refusedInRun("gog drive unshare", "gog_drive_run", "removes someone's access to a file", "Use gog_drive_unshare.");
|
|
38081
|
+
}
|
|
38082
|
+
return vetCommentsRun("drive", sub, args);
|
|
37729
38083
|
}
|
|
37730
38084
|
var GOOGLE_DOC_MIME = "application/vnd.google-apps.document";
|
|
37731
38085
|
function shareTargetMeta(raw) {
|
|
@@ -37844,16 +38198,36 @@ function registerDriveTools(server) {
|
|
|
37844
38198
|
return runOrDiagnose(["drive", "move", pos(fileId), `--parent=${parentId}`], { account });
|
|
37845
38199
|
});
|
|
37846
38200
|
server.registerTool("gog_drive_delete", {
|
|
37847
|
-
description: "Move a Google Drive file to trash, or permanently delete it with permanent=true (irreversible).",
|
|
38201
|
+
description: "Move a Google Drive file to trash, or permanently delete it with permanent=true (irreversible). A permanent delete reads the file and asks the MCP host to show the user a confirmation prompt naming it first; nothing is deleted unless they accept. Moving to trash is recoverable and does not ask." + CONFIRM_FALLBACK_DESCRIPTION,
|
|
37848
38202
|
annotations: { destructiveHint: true },
|
|
37849
38203
|
inputSchema: external_exports.object({
|
|
37850
38204
|
fileId: external_exports.string().describe("File ID to delete"),
|
|
37851
|
-
permanent: external_exports.boolean().optional().describe("Permanently delete instead of moving to trash (irreversible)"),
|
|
37852
|
-
account: accountParam
|
|
38205
|
+
permanent: external_exports.boolean().optional().describe("Permanently delete instead of moving to trash (irreversible; asks the user first)"),
|
|
38206
|
+
account: accountParam,
|
|
38207
|
+
confirmToken: confirmTokenParam
|
|
37853
38208
|
})
|
|
37854
|
-
}, async ({ fileId, permanent, account }) => {
|
|
38209
|
+
}, async ({ fileId, permanent, account, confirmToken }, ctx) => {
|
|
37855
38210
|
const args = ["drive", "delete", pos(fileId)];
|
|
37856
|
-
if (permanent)
|
|
38211
|
+
if (permanent) {
|
|
38212
|
+
const got = await runOrDiagnose(["drive", "get", pos(fileId)], { account });
|
|
38213
|
+
if (got.isError) return got;
|
|
38214
|
+
const target = { file: { id: fileId, ...shareTargetMeta(resultText(got)) }, permanent: true };
|
|
38215
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
38216
|
+
action: "drive.delete-permanent",
|
|
38217
|
+
message: "Review and confirm PERMANENTLY deleting this file \u2014 it skips the trash and cannot be recovered:",
|
|
38218
|
+
confirmationLabel: "Confirm that this file should be permanently deleted now.",
|
|
38219
|
+
details: target,
|
|
38220
|
+
unsupportedNote: "Move it to the trash instead (permanent=false); the user can empty the trash from Google Drive.",
|
|
38221
|
+
fallback: {
|
|
38222
|
+
tool: "gog_drive_delete",
|
|
38223
|
+
account,
|
|
38224
|
+
confirmToken,
|
|
38225
|
+
subject: () => ({ target: fileId, payload: target, preview: target })
|
|
38226
|
+
}
|
|
38227
|
+
});
|
|
38228
|
+
if (confirmation) return confirmation;
|
|
38229
|
+
args.push("--permanent");
|
|
38230
|
+
}
|
|
37857
38231
|
args.push("--force");
|
|
37858
38232
|
return runOrDiagnose(args, { account });
|
|
37859
38233
|
});
|
|
@@ -38179,18 +38553,22 @@ function trustedDomains(account) {
|
|
|
38179
38553
|
}
|
|
38180
38554
|
function logGmailDispatch(tool, recipients, account) {
|
|
38181
38555
|
const domains = trustedDomains(account);
|
|
38182
|
-
const
|
|
38556
|
+
const externalDomains = /* @__PURE__ */ new Set();
|
|
38557
|
+
let externalRecipientCount = 0;
|
|
38558
|
+
for (const recipient of recipients) {
|
|
38183
38559
|
const at = recipient.indexOf("@");
|
|
38184
|
-
const domain2 = at > -1 ? recipient.slice(at + 1) : "";
|
|
38185
|
-
|
|
38186
|
-
|
|
38560
|
+
const domain2 = at > -1 ? recipient.slice(at + 1).toLowerCase() : "";
|
|
38561
|
+
if (domain2 && domains.has(domain2)) continue;
|
|
38562
|
+
externalRecipientCount += 1;
|
|
38563
|
+
externalDomains.add(domain2 || "(no domain)");
|
|
38564
|
+
}
|
|
38187
38565
|
const event = {
|
|
38188
38566
|
event: "gmail_dispatch",
|
|
38189
38567
|
tool,
|
|
38190
38568
|
recipientCount: recipients.length,
|
|
38191
|
-
externalRecipientCount
|
|
38192
|
-
hasExternalRecipients:
|
|
38193
|
-
|
|
38569
|
+
externalRecipientCount,
|
|
38570
|
+
hasExternalRecipients: externalRecipientCount > 0,
|
|
38571
|
+
externalDomains: [...externalDomains].sort(),
|
|
38194
38572
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
38195
38573
|
};
|
|
38196
38574
|
process.stderr.write(`${JSON.stringify(event)}
|
|
@@ -38357,6 +38735,39 @@ async function sendReply(kind, toolName, messageId, account, flags, ctx, confirm
|
|
|
38357
38735
|
return result;
|
|
38358
38736
|
}
|
|
38359
38737
|
var GMAIL_RUN_BLOCKED_SETTINGS = /* @__PURE__ */ new Set(["forwarding", "autoforward", "filters", "delegates"]);
|
|
38738
|
+
var GMAIL_DELETE_WORDS = /* @__PURE__ */ new Set(["delete", "rm", "del", "remove"]);
|
|
38739
|
+
var GMAIL_CREATE_WORDS = /* @__PURE__ */ new Set(["create", "add", "new"]);
|
|
38740
|
+
var GMAIL_UPDATE_WORDS = /* @__PURE__ */ new Set(["update", "edit", "set"]);
|
|
38741
|
+
var SENDAS = /* @__PURE__ */ new Set(["sendas"]);
|
|
38742
|
+
var VACATION = /* @__PURE__ */ new Set(["vacation"]);
|
|
38743
|
+
function vetGmailGatedRun(sub, args) {
|
|
38744
|
+
if (sub === "batch") {
|
|
38745
|
+
const word = hasCommandWord(args, GMAIL_DELETE_WORDS);
|
|
38746
|
+
return word ? gatedElsewhere(`gog gmail batch ${word.toLowerCase()}`, "gog_gmail_run", "deletes mail for good, bypassing the Trash", "gog_gmail_batch_delete") : void 0;
|
|
38747
|
+
}
|
|
38748
|
+
if (sub !== "settings" && sub !== "sendas" && sub !== "vacation") return void 0;
|
|
38749
|
+
const words = sub === "settings" ? args : [sub, ...args];
|
|
38750
|
+
if (hasCommandWord(words, SENDAS)) {
|
|
38751
|
+
const word = hasCommandWord(args, GMAIL_CREATE_WORDS);
|
|
38752
|
+
if (word) {
|
|
38753
|
+
return gatedElsewhere(
|
|
38754
|
+
`gog gmail sendas ${word.toLowerCase()}`,
|
|
38755
|
+
"gog_gmail_run",
|
|
38756
|
+
"makes Google email the address and adds a sending identity",
|
|
38757
|
+
"gog_gmail_sendas_create"
|
|
38758
|
+
);
|
|
38759
|
+
}
|
|
38760
|
+
}
|
|
38761
|
+
if (hasCommandWord(words, VACATION) && hasCommandWord(args, GMAIL_UPDATE_WORDS) && hasTrueFlag(args, "enable")) {
|
|
38762
|
+
return gatedElsewhere(
|
|
38763
|
+
"gog gmail vacation update --enable",
|
|
38764
|
+
"gog_gmail_run",
|
|
38765
|
+
"turns on an auto-reply to every sender",
|
|
38766
|
+
"gog_gmail_vacation_update"
|
|
38767
|
+
);
|
|
38768
|
+
}
|
|
38769
|
+
return void 0;
|
|
38770
|
+
}
|
|
38360
38771
|
function vetGmailRun(subcommand, args) {
|
|
38361
38772
|
if (subcommand === "autoreply") {
|
|
38362
38773
|
return "gog gmail autoreply sends mail and is not available through gog_gmail_run. Use gog_gmail_autoreply, which asks the user to confirm.";
|
|
@@ -38364,6 +38775,8 @@ function vetGmailRun(subcommand, args) {
|
|
|
38364
38775
|
if (GMAIL_RUN_BLOCKED_SETTINGS.has(subcommand)) {
|
|
38365
38776
|
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.`;
|
|
38366
38777
|
}
|
|
38778
|
+
const gated = vetGmailGatedRun(subcommand.toLowerCase(), args);
|
|
38779
|
+
if (gated) return gated;
|
|
38367
38780
|
if (subcommand === "settings") {
|
|
38368
38781
|
const blocked = hasCommandWord(args, GMAIL_RUN_BLOCKED_SETTINGS);
|
|
38369
38782
|
if (blocked) {
|
|
@@ -38575,6 +38988,13 @@ function countNonEmptyCells(getOutput) {
|
|
|
38575
38988
|
}
|
|
38576
38989
|
|
|
38577
38990
|
// src/tools/sheets.ts
|
|
38991
|
+
var DATASOURCE_ALIASES = /* @__PURE__ */ new Set(["datasource", "data-source", "data-sources", "connected-sheets"]);
|
|
38992
|
+
var DATASOURCE_BILLED = /* @__PURE__ */ new Set(["add", "update", "refresh"]);
|
|
38993
|
+
function vetSheetsRun(subcommand, args) {
|
|
38994
|
+
if (!DATASOURCE_ALIASES.has(subcommand.toLowerCase())) return void 0;
|
|
38995
|
+
const word = hasCommandWord(args, DATASOURCE_BILLED)?.toLowerCase();
|
|
38996
|
+
return word ? gatedElsewhere(`gog sheets datasource ${word}`, "gog_sheets_run", "runs a billed BigQuery job", `gog_sheets_datasource_${word}`) : void 0;
|
|
38997
|
+
}
|
|
38578
38998
|
var cellValueParam = external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean(), external_exports.null()]);
|
|
38579
38999
|
var dryRunParam = external_exports.boolean().optional().describe(
|
|
38580
39000
|
"Preview the operation without modifying the sheet (gog --dry-run): reports the intended actions and exits without writing."
|
|
@@ -38690,7 +39110,7 @@ function registerSheetsTools(server) {
|
|
|
38690
39110
|
}, async ({ spreadsheetId, find, replace, account }) => {
|
|
38691
39111
|
return runOrDiagnose(["sheets", "find-replace", pos(spreadsheetId), pos(find), pos(replace)], { account });
|
|
38692
39112
|
});
|
|
38693
|
-
registerRunTool(server, { service: "sheets", examples: '"freeze", "add-tab", "rename-tab"' });
|
|
39113
|
+
registerRunTool(server, { service: "sheets", examples: '"freeze", "add-tab", "rename-tab"', vet: vetSheetsRun });
|
|
38694
39114
|
}
|
|
38695
39115
|
|
|
38696
39116
|
// src/tools/slides.ts
|
|
@@ -38854,7 +39274,7 @@ function registerTasksTools(server) {
|
|
|
38854
39274
|
}
|
|
38855
39275
|
|
|
38856
39276
|
// src/server.ts
|
|
38857
|
-
var VERSION = true ? "4.5.
|
|
39277
|
+
var VERSION = true ? "4.5.1" : "0.0.0";
|
|
38858
39278
|
var BASE_TOOL_REGISTRARS = [
|
|
38859
39279
|
registerApiTools,
|
|
38860
39280
|
registerAppScriptTools,
|