gogcli-mcp 4.5.0 → 4.5.2
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 +659 -162
- package/dist/lib.js +619 -142
- package/manifest.json +7 -7
- package/package.json +3 -3
- 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
|
}
|
|
@@ -19790,7 +19790,7 @@ function date4(params) {
|
|
|
19790
19790
|
return _coercedDate(ZodDate, params);
|
|
19791
19791
|
}
|
|
19792
19792
|
|
|
19793
|
-
// ../../node_modules/@modelcontextprotocol/core/dist/auth-
|
|
19793
|
+
// ../../node_modules/@modelcontextprotocol/server/node_modules/@modelcontextprotocol/core/dist/auth-CGP0BDVq.mjs
|
|
19794
19794
|
var LATEST_PROTOCOL_VERSION = "2025-11-25";
|
|
19795
19795
|
var SUPPORTED_PROTOCOL_VERSIONS = [
|
|
19796
19796
|
LATEST_PROTOCOL_VERSION,
|
|
@@ -20627,7 +20627,8 @@ var OAuthMetadataSchema = looseObject({
|
|
|
20627
20627
|
introspection_endpoint_auth_signing_alg_values_supported: array(string2()).optional(),
|
|
20628
20628
|
code_challenge_methods_supported: array(string2()).optional(),
|
|
20629
20629
|
client_id_metadata_document_supported: boolean2().optional(),
|
|
20630
|
-
authorization_response_iss_parameter_supported: boolean2().optional().catch(void 0)
|
|
20630
|
+
authorization_response_iss_parameter_supported: boolean2().optional().catch(void 0),
|
|
20631
|
+
dpop_signing_alg_values_supported: array(string2()).optional()
|
|
20631
20632
|
});
|
|
20632
20633
|
var OpenIdProviderMetadataSchema = looseObject({
|
|
20633
20634
|
issuer: string2(),
|
|
@@ -20728,7 +20729,7 @@ var OAuthTokenRevocationRequestSchema = object({
|
|
|
20728
20729
|
token_type_hint: string2().optional()
|
|
20729
20730
|
}).strip();
|
|
20730
20731
|
|
|
20731
|
-
// ../../node_modules/@modelcontextprotocol/server/dist/src-
|
|
20732
|
+
// ../../node_modules/@modelcontextprotocol/server/dist/src-D-y6h4N7.mjs
|
|
20732
20733
|
var BRANDS = /* @__PURE__ */ Symbol.for("mcp.sdk.errorBrands");
|
|
20733
20734
|
function stampErrorBrands(instance, ctor) {
|
|
20734
20735
|
const brands = /* @__PURE__ */ new Set();
|
|
@@ -20842,8 +20843,18 @@ var SdkError = class extends Error {
|
|
|
20842
20843
|
if (typeof this !== "function") throw new TypeError("isInstance must be called on the class (e.g. `SdkError.isInstance(value)`); for callbacks use `v => SdkError.isInstance(v)`");
|
|
20843
20844
|
return brandedHasInstance(this, value);
|
|
20844
20845
|
}
|
|
20845
|
-
|
|
20846
|
-
|
|
20846
|
+
/**
|
|
20847
|
+
* @param code - Stable string code identifying the failure ({@linkcode SdkErrorCode}).
|
|
20848
|
+
* @param message - Human-readable description.
|
|
20849
|
+
* @param data - Optional structured payload (for example the HTTP status carried by
|
|
20850
|
+
* {@linkcode SdkHttpError}). Opaque to the SDK: a `cause` key inside `data` is not
|
|
20851
|
+
* promoted to `Error.cause`.
|
|
20852
|
+
* @param options - Standard `ErrorOptions`, forwarded to `Error`. Pass the underlying
|
|
20853
|
+
* failure as `{ cause }` so it is reachable through the `Error.cause` chain that
|
|
20854
|
+
* loggers and error trackers walk.
|
|
20855
|
+
*/
|
|
20856
|
+
constructor(code, message, data, options) {
|
|
20857
|
+
super(message, options);
|
|
20847
20858
|
this.code = code;
|
|
20848
20859
|
this.data = data;
|
|
20849
20860
|
this.name = "SdkError";
|
|
@@ -20854,8 +20865,11 @@ var SdkHttpError = class extends SdkError {
|
|
|
20854
20865
|
static {
|
|
20855
20866
|
Object.defineProperty(this, "mcpBrand", { value: "mcp.SdkHttpError" });
|
|
20856
20867
|
}
|
|
20857
|
-
|
|
20858
|
-
|
|
20868
|
+
/**
|
|
20869
|
+
* @param options - Standard `ErrorOptions`, forwarded to `Error` (see {@linkcode SdkError}).
|
|
20870
|
+
*/
|
|
20871
|
+
constructor(code, message, data, options) {
|
|
20872
|
+
super(code, message, data, options);
|
|
20859
20873
|
this.name = "SdkHttpError";
|
|
20860
20874
|
}
|
|
20861
20875
|
get status() {
|
|
@@ -23806,7 +23820,7 @@ var INBOUND_VALIDATION_LADDER = [
|
|
|
23806
23820
|
evaluatedAt: "pre-dispatch",
|
|
23807
23821
|
codes: [HEADER_MISMATCH_ERROR_CODE],
|
|
23808
23822
|
conformance: ["http-header-validation"],
|
|
23809
|
-
rationale: "SEP-2243 standard `Mcp-Method` / `Mcp-Name` headers \u2014 presence, sentinel decoding, and `Mcp-Name` \u2194 body cross-check \u2014 are validated by the HTTP entry on a modern-classified request after the supported-revision gate and before dispatch. The classifier\u2019s own header-mismatch cells (protocol-version, `Mcp-Method` mismatch) stay on the edge `era-classification` rung; this rung carries the entry-layer presence/`Mcp-Name` half. Evaluated before the capability gate, the factory call, and the `Mcp-Param-*` rung so a request that fails several rungs is answered by the standard-header rung first. The documented order (after method-registry 5 and request-params 6) is NOT the observed precedence: serveModern evaluates this rung immediately after the supported-revision gate, so a request that also fails a dispatch rung is answered here before the dispatch rungs (5\u20136) are consulted."
|
|
23823
|
+
rationale: "SEP-2243 standard `MCP-Protocol-Version` / `Mcp-Method` / `Mcp-Name` headers \u2014 presence, sentinel decoding, and `Mcp-Name` \u2194 body cross-check \u2014 are validated by the HTTP entry on a modern-classified request after the supported-revision gate and before dispatch. The spec requires `MCP-Protocol-Version` and `Mcp-Method` on every modern *request* POST (`Mcp-Name` only for the methods that mirror `params.name` / `params.uri` / `params.taskId`, see `MCP_NAME_HEADER_SOURCE`) and names them in that order, so a request missing several is answered by the earliest. Notification POSTs are exempt: the presence half runs on requests only, so a modern-enveloped notification is dispatched even with no standard headers at all. The classifier\u2019s own header-mismatch cells (protocol-version, `Mcp-Method` mismatch) stay on the edge `era-classification` rung; this rung carries the entry-layer presence/`Mcp-Name` half \u2014 including the missing `MCP-Protocol-Version` cell, which cannot live on the edge rung without breaking body-primary classification. Evaluated before the capability gate, the factory call, and the `Mcp-Param-*` rung so a request that fails several rungs is answered by the standard-header rung first. The documented order (after method-registry 5 and request-params 6) is NOT the observed precedence: serveModern evaluates this rung immediately after the supported-revision gate, so a request that also fails a dispatch rung is answered here before the dispatch rungs (5\u20136) are consulted."
|
|
23810
23824
|
},
|
|
23811
23825
|
{
|
|
23812
23826
|
rung: "client-capabilities",
|
|
@@ -24607,7 +24621,7 @@ var Protocol = class {
|
|
|
24607
24621
|
return this._requestHandlers.get(method);
|
|
24608
24622
|
}
|
|
24609
24623
|
async _oncancel(notification) {
|
|
24610
|
-
if (
|
|
24624
|
+
if (notification.params.requestId === void 0) return;
|
|
24611
24625
|
this._requestHandlerAbortControllers.get(notification.params.requestId)?.abort(notification.params.reason);
|
|
24612
24626
|
}
|
|
24613
24627
|
_setupTimeout(messageId, timeout, maxTotalTimeout, onTimeout, resetTimeoutOnProgress = false) {
|
|
@@ -25006,19 +25020,20 @@ var Protocol = class {
|
|
|
25006
25020
|
const cancel = (reason) => {
|
|
25007
25021
|
if (responseReceived) return;
|
|
25008
25022
|
this._progressHandlers.delete(messageId);
|
|
25009
|
-
if (requestAbort === void 0)
|
|
25010
|
-
|
|
25011
|
-
|
|
25012
|
-
|
|
25013
|
-
|
|
25014
|
-
|
|
25015
|
-
|
|
25016
|
-
|
|
25017
|
-
|
|
25018
|
-
|
|
25019
|
-
|
|
25020
|
-
|
|
25021
|
-
|
|
25023
|
+
if (requestAbort === void 0) {
|
|
25024
|
+
if (request.method !== "initialize") this._transport?.send(this._envelopeOutbound({
|
|
25025
|
+
jsonrpc: "2.0",
|
|
25026
|
+
method: "notifications/cancelled",
|
|
25027
|
+
params: {
|
|
25028
|
+
requestId: messageId,
|
|
25029
|
+
reason: String(reason)
|
|
25030
|
+
}
|
|
25031
|
+
}), {
|
|
25032
|
+
relatedRequestId,
|
|
25033
|
+
resumptionToken,
|
|
25034
|
+
onresumptiontoken
|
|
25035
|
+
}).catch((error62) => this._onerror(/* @__PURE__ */ new Error(`Failed to send cancellation: ${error62}`)));
|
|
25036
|
+
} else requestAbort.abort();
|
|
25022
25037
|
reject(reason instanceof SdkError ? reason : new SdkError(SdkErrorCode.RequestTimeout, String(reason)));
|
|
25023
25038
|
};
|
|
25024
25039
|
this._responseHandlers.set(messageId, (response) => {
|
|
@@ -25098,7 +25113,7 @@ var Protocol = class {
|
|
|
25098
25113
|
jsonrpc: "2.0",
|
|
25099
25114
|
...notification
|
|
25100
25115
|
});
|
|
25101
|
-
if ((this._options?.debouncedNotificationMethods ?? []).includes(notification.method) && !notification.params &&
|
|
25116
|
+
if ((this._options?.debouncedNotificationMethods ?? []).includes(notification.method) && !notification.params && options?.relatedRequestId === void 0) {
|
|
25102
25117
|
if (this._pendingDebouncedNotifications.has(notification.method)) return;
|
|
25103
25118
|
this._pendingDebouncedNotifications.add(notification.method);
|
|
25104
25119
|
Promise.resolve().then(() => {
|
|
@@ -25451,7 +25466,7 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
25451
25466
|
}
|
|
25452
25467
|
exports._ = _;
|
|
25453
25468
|
const plus = new _Code("+");
|
|
25454
|
-
function
|
|
25469
|
+
function str2(strs, ...args) {
|
|
25455
25470
|
const expr = [safeStringify(strs[0])];
|
|
25456
25471
|
let i = 0;
|
|
25457
25472
|
while (i < args.length) {
|
|
@@ -25462,7 +25477,7 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
25462
25477
|
optimize(expr);
|
|
25463
25478
|
return new _Code(expr);
|
|
25464
25479
|
}
|
|
25465
|
-
exports.str =
|
|
25480
|
+
exports.str = str2;
|
|
25466
25481
|
function addCodeArg(code, arg) {
|
|
25467
25482
|
if (arg instanceof _Code) code.push(...arg._items);
|
|
25468
25483
|
else if (arg instanceof Name) code.push(arg);
|
|
@@ -25495,7 +25510,7 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
25495
25510
|
if (typeof b == "string" && b[0] === '"' && !(a instanceof Name)) return `"${a}${b.slice(1)}`;
|
|
25496
25511
|
}
|
|
25497
25512
|
function strConcat(c1, c2) {
|
|
25498
|
-
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 :
|
|
25513
|
+
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str2`${c1}${c2}`;
|
|
25499
25514
|
}
|
|
25500
25515
|
exports.strConcat = strConcat;
|
|
25501
25516
|
function interpolate(x) {
|
|
@@ -26374,21 +26389,21 @@ var require_util = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
26374
26389
|
return (0, codegen_1._)`${topSchemaRef}${schemaPath}${(0, codegen_1.getProperty)(keyword)}`;
|
|
26375
26390
|
}
|
|
26376
26391
|
exports.schemaRefOrVal = schemaRefOrVal;
|
|
26377
|
-
function unescapeFragment(
|
|
26378
|
-
return unescapeJsonPointer(decodeURIComponent(
|
|
26392
|
+
function unescapeFragment(str2) {
|
|
26393
|
+
return unescapeJsonPointer(decodeURIComponent(str2));
|
|
26379
26394
|
}
|
|
26380
26395
|
exports.unescapeFragment = unescapeFragment;
|
|
26381
|
-
function escapeFragment(
|
|
26382
|
-
return encodeURIComponent(escapeJsonPointer(
|
|
26396
|
+
function escapeFragment(str2) {
|
|
26397
|
+
return encodeURIComponent(escapeJsonPointer(str2));
|
|
26383
26398
|
}
|
|
26384
26399
|
exports.escapeFragment = escapeFragment;
|
|
26385
|
-
function escapeJsonPointer(
|
|
26386
|
-
if (typeof
|
|
26387
|
-
return
|
|
26400
|
+
function escapeJsonPointer(str2) {
|
|
26401
|
+
if (typeof str2 == "number") return `${str2}`;
|
|
26402
|
+
return str2.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
26388
26403
|
}
|
|
26389
26404
|
exports.escapeJsonPointer = escapeJsonPointer;
|
|
26390
|
-
function unescapeJsonPointer(
|
|
26391
|
-
return
|
|
26405
|
+
function unescapeJsonPointer(str2) {
|
|
26406
|
+
return str2.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
26392
26407
|
}
|
|
26393
26408
|
exports.unescapeJsonPointer = unescapeJsonPointer;
|
|
26394
26409
|
function eachItem(xs, f) {
|
|
@@ -27274,8 +27289,8 @@ var require_json_schema_traverse = /* @__PURE__ */ __commonJSMin(((exports, modu
|
|
|
27274
27289
|
post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
|
|
27275
27290
|
}
|
|
27276
27291
|
}
|
|
27277
|
-
function escapeJsonPtr(
|
|
27278
|
-
return
|
|
27292
|
+
function escapeJsonPtr(str2) {
|
|
27293
|
+
return str2.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
27279
27294
|
}
|
|
27280
27295
|
}));
|
|
27281
27296
|
var require_resolve = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
@@ -28167,9 +28182,9 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
28167
28182
|
isIPV6: false
|
|
28168
28183
|
};
|
|
28169
28184
|
}
|
|
28170
|
-
function findToken(
|
|
28185
|
+
function findToken(str2, token) {
|
|
28171
28186
|
let ind = 0;
|
|
28172
|
-
for (let i = 0; i <
|
|
28187
|
+
for (let i = 0; i < str2.length; i++) if (str2[i] === token) ind++;
|
|
28173
28188
|
return ind;
|
|
28174
28189
|
}
|
|
28175
28190
|
function removeDotSegments(path) {
|
|
@@ -28684,7 +28699,7 @@ var require_core$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
28684
28699
|
const util_1 = require_util();
|
|
28685
28700
|
const $dataRefSchema = require_data();
|
|
28686
28701
|
const uri_1 = require_uri();
|
|
28687
|
-
const defaultRegExp = (
|
|
28702
|
+
const defaultRegExp = (str2, flags) => new RegExp(str2, flags);
|
|
28688
28703
|
defaultRegExp.code = "new RegExp";
|
|
28689
28704
|
const META_IGNORE_OPTIONS = [
|
|
28690
28705
|
"removeAdditional",
|
|
@@ -29390,16 +29405,16 @@ var require_multipleOf = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
29390
29405
|
}));
|
|
29391
29406
|
var require_ucs2length = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
29392
29407
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29393
|
-
function ucs2length(
|
|
29394
|
-
const len =
|
|
29408
|
+
function ucs2length(str2) {
|
|
29409
|
+
const len = str2.length;
|
|
29395
29410
|
let length = 0;
|
|
29396
29411
|
let pos2 = 0;
|
|
29397
29412
|
let value;
|
|
29398
29413
|
while (pos2 < len) {
|
|
29399
29414
|
length++;
|
|
29400
|
-
value =
|
|
29415
|
+
value = str2.charCodeAt(pos2++);
|
|
29401
29416
|
if (value >= 55296 && value <= 56319 && pos2 < len) {
|
|
29402
|
-
value =
|
|
29417
|
+
value = str2.charCodeAt(pos2);
|
|
29403
29418
|
if ((value & 64512) === 56320) pos2++;
|
|
29404
29419
|
}
|
|
29405
29420
|
}
|
|
@@ -32110,8 +32125,8 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
32110
32125
|
30,
|
|
32111
32126
|
31
|
|
32112
32127
|
];
|
|
32113
|
-
function date5(
|
|
32114
|
-
const matches = DATE.exec(
|
|
32128
|
+
function date5(str2) {
|
|
32129
|
+
const matches = DATE.exec(str2);
|
|
32115
32130
|
if (!matches) return false;
|
|
32116
32131
|
const year = +matches[1];
|
|
32117
32132
|
const month = +matches[2];
|
|
@@ -32126,8 +32141,8 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
32126
32141
|
}
|
|
32127
32142
|
const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
|
|
32128
32143
|
function getTime(strictTimeZone) {
|
|
32129
|
-
return function time3(
|
|
32130
|
-
const matches = TIME.exec(
|
|
32144
|
+
return function time3(str2) {
|
|
32145
|
+
const matches = TIME.exec(str2);
|
|
32131
32146
|
if (!matches) return false;
|
|
32132
32147
|
const hr = +matches[1];
|
|
32133
32148
|
const min = +matches[2];
|
|
@@ -32164,8 +32179,8 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
32164
32179
|
const DATE_TIME_SEPARATOR = /t|\s/i;
|
|
32165
32180
|
function getDateTime(strictTimeZone) {
|
|
32166
32181
|
const time3 = getTime(strictTimeZone);
|
|
32167
|
-
return function date_time(
|
|
32168
|
-
const dateTime =
|
|
32182
|
+
return function date_time(str2) {
|
|
32183
|
+
const dateTime = str2.split(DATE_TIME_SEPARATOR);
|
|
32169
32184
|
return dateTime.length === 2 && date5(dateTime[0]) && time3(dateTime[1]);
|
|
32170
32185
|
};
|
|
32171
32186
|
}
|
|
@@ -32186,13 +32201,13 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
32186
32201
|
}
|
|
32187
32202
|
const NOT_URI_FRAGMENT = /\/|:/;
|
|
32188
32203
|
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(
|
|
32204
|
+
function uri(str2) {
|
|
32205
|
+
return NOT_URI_FRAGMENT.test(str2) && URI.test(str2);
|
|
32191
32206
|
}
|
|
32192
32207
|
const BYTE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gm;
|
|
32193
|
-
function byte(
|
|
32208
|
+
function byte(str2) {
|
|
32194
32209
|
BYTE.lastIndex = 0;
|
|
32195
|
-
return BYTE.test(
|
|
32210
|
+
return BYTE.test(str2);
|
|
32196
32211
|
}
|
|
32197
32212
|
const MIN_INT32 = -(2 ** 31);
|
|
32198
32213
|
const MAX_INT32 = 2 ** 31 - 1;
|
|
@@ -32206,10 +32221,10 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
32206
32221
|
return true;
|
|
32207
32222
|
}
|
|
32208
32223
|
const Z_ANCHOR = /[^\\]\\Z/;
|
|
32209
|
-
function regex(
|
|
32210
|
-
if (Z_ANCHOR.test(
|
|
32224
|
+
function regex(str2) {
|
|
32225
|
+
if (Z_ANCHOR.test(str2)) return false;
|
|
32211
32226
|
try {
|
|
32212
|
-
new RegExp(
|
|
32227
|
+
new RegExp(str2);
|
|
32213
32228
|
return true;
|
|
32214
32229
|
} catch (e) {
|
|
32215
32230
|
return false;
|
|
@@ -32401,7 +32416,7 @@ var Ajv = import_ajv.Ajv;
|
|
|
32401
32416
|
// ../../node_modules/@modelcontextprotocol/server/dist/shimsNode.mjs
|
|
32402
32417
|
import process2 from "node:process";
|
|
32403
32418
|
|
|
32404
|
-
// ../../node_modules/@modelcontextprotocol/server/dist/mcp-
|
|
32419
|
+
// ../../node_modules/@modelcontextprotocol/server/dist/mcp-Dw2OlZ1f.mjs
|
|
32405
32420
|
var COMPLETABLE_SYMBOL = /* @__PURE__ */ Symbol.for("mcp.completable");
|
|
32406
32421
|
function isCompletable(schema) {
|
|
32407
32422
|
return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
|
|
@@ -32594,6 +32609,9 @@ function notificationToServerEvent(method, uri) {
|
|
|
32594
32609
|
};
|
|
32595
32610
|
}
|
|
32596
32611
|
}
|
|
32612
|
+
function supportsScopeChallengeResolver(transport) {
|
|
32613
|
+
return typeof transport === "object" && transport !== null && "setScopeChallengeResolver" in transport && typeof transport.setScopeChallengeResolver === "function";
|
|
32614
|
+
}
|
|
32597
32615
|
var DEFAULT_LEGACY_SHIM_MAX_ROUNDS = 8;
|
|
32598
32616
|
var DEFAULT_LEGACY_SHIM_ROUND_TIMEOUT_MS = 6e5;
|
|
32599
32617
|
function resolveLegacyShimOptions(options) {
|
|
@@ -33404,6 +33422,7 @@ var McpServer = class {
|
|
|
33404
33422
|
* ```
|
|
33405
33423
|
*/
|
|
33406
33424
|
async connect(transport) {
|
|
33425
|
+
if (supportsScopeChallengeResolver(transport)) transport.setScopeChallengeResolver((context) => this.resolveScopeChallenge(context));
|
|
33407
33426
|
return await this.server.connect(transport);
|
|
33408
33427
|
}
|
|
33409
33428
|
/**
|
|
@@ -33412,6 +33431,41 @@ var McpServer = class {
|
|
|
33412
33431
|
async close() {
|
|
33413
33432
|
await this.server.close();
|
|
33414
33433
|
}
|
|
33434
|
+
/** @internal */
|
|
33435
|
+
resolveScopeChallenge = (context) => {
|
|
33436
|
+
switch (context.request.method) {
|
|
33437
|
+
case "tools/call": {
|
|
33438
|
+
const toolName = context.request.params?.name;
|
|
33439
|
+
if (typeof toolName !== "string") return;
|
|
33440
|
+
const tool = this._registeredTools[toolName];
|
|
33441
|
+
if (tool === void 0 || !tool.enabled) return;
|
|
33442
|
+
return tool.scopeChallenge?.(context);
|
|
33443
|
+
}
|
|
33444
|
+
case "resources/read": {
|
|
33445
|
+
const resourceUri = context.request.params?.uri;
|
|
33446
|
+
if (typeof resourceUri !== "string") return;
|
|
33447
|
+
let uri;
|
|
33448
|
+
try {
|
|
33449
|
+
uri = new URL(resourceUri);
|
|
33450
|
+
} catch {
|
|
33451
|
+
return;
|
|
33452
|
+
}
|
|
33453
|
+
const resource = this._registeredResources[uri.toString()];
|
|
33454
|
+
if (resource !== void 0) return resource.enabled ? resource.scopeChallenge?.(context) : void 0;
|
|
33455
|
+
for (const template of Object.values(this._registeredResourceTemplates)) if (template.resourceTemplate.uriTemplate.match(uri.toString())) return template.enabled ? template.scopeChallenge?.(context) : void 0;
|
|
33456
|
+
return;
|
|
33457
|
+
}
|
|
33458
|
+
case "prompts/get": {
|
|
33459
|
+
const promptName = context.request.params?.name;
|
|
33460
|
+
if (typeof promptName !== "string") return;
|
|
33461
|
+
const prompt = this._registeredPrompts[promptName];
|
|
33462
|
+
if (prompt === void 0 || !prompt.enabled) return;
|
|
33463
|
+
return prompt.scopeChallenge?.(context);
|
|
33464
|
+
}
|
|
33465
|
+
default:
|
|
33466
|
+
return;
|
|
33467
|
+
}
|
|
33468
|
+
};
|
|
33415
33469
|
_toolHandlersInitialized = false;
|
|
33416
33470
|
setToolRequestHandlers() {
|
|
33417
33471
|
if (this._toolHandlersInitialized) return;
|
|
@@ -33578,7 +33632,10 @@ var McpServer = class {
|
|
|
33578
33632
|
}
|
|
33579
33633
|
for (const template of Object.values(this._registeredResourceTemplates)) {
|
|
33580
33634
|
const variables = template.resourceTemplate.uriTemplate.match(uri.toString());
|
|
33581
|
-
if (variables)
|
|
33635
|
+
if (variables) {
|
|
33636
|
+
if (!template.enabled) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Resource template ${template.resourceTemplate.uriTemplate} disabled`);
|
|
33637
|
+
return attachCacheHintFallback(await template.readCallback(uri, variables, ctx), template.cacheHint);
|
|
33638
|
+
}
|
|
33582
33639
|
}
|
|
33583
33640
|
throw new ResourceNotFoundError(request.params.uri);
|
|
33584
33641
|
});
|
|
@@ -33609,36 +33666,32 @@ var McpServer = class {
|
|
|
33609
33666
|
this._promptHandlersInitialized = true;
|
|
33610
33667
|
}
|
|
33611
33668
|
registerResource(name, uriOrTemplate, config2, readCallback) {
|
|
33612
|
-
const cacheHint = config2
|
|
33613
|
-
|
|
33614
|
-
if (cacheHint !== void 0) {
|
|
33615
|
-
assertValidCacheHint(cacheHint, `resource ${name}`);
|
|
33616
|
-
const rest = { ...config2 };
|
|
33617
|
-
delete rest.cacheHint;
|
|
33618
|
-
metadata = rest;
|
|
33619
|
-
}
|
|
33669
|
+
const { cacheHint, scopeChallenge, ...resourceMetadata } = config2;
|
|
33670
|
+
const metadata = resourceMetadata;
|
|
33671
|
+
if (cacheHint !== void 0) assertValidCacheHint(cacheHint, `resource ${name}`);
|
|
33620
33672
|
if (typeof uriOrTemplate === "string") {
|
|
33621
33673
|
if (this._registeredResources[uriOrTemplate]) throw new Error(`Resource ${uriOrTemplate} is already registered`);
|
|
33622
|
-
const registeredResource = this._createRegisteredResource(name, config2.title, uriOrTemplate, metadata, readCallback);
|
|
33674
|
+
const registeredResource = this._createRegisteredResource(name, config2.title, uriOrTemplate, metadata, scopeChallenge, readCallback);
|
|
33623
33675
|
if (cacheHint !== void 0) registeredResource.cacheHint = cacheHint;
|
|
33624
33676
|
this.setResourceRequestHandlers();
|
|
33625
33677
|
this.sendResourceListChanged();
|
|
33626
33678
|
return registeredResource;
|
|
33627
33679
|
} else {
|
|
33628
33680
|
if (this._registeredResourceTemplates[name]) throw new Error(`Resource template ${name} is already registered`);
|
|
33629
|
-
const registeredResourceTemplate = this._createRegisteredResourceTemplate(name, config2.title, uriOrTemplate, metadata, readCallback);
|
|
33681
|
+
const registeredResourceTemplate = this._createRegisteredResourceTemplate(name, config2.title, uriOrTemplate, metadata, scopeChallenge, readCallback);
|
|
33630
33682
|
if (cacheHint !== void 0) registeredResourceTemplate.cacheHint = cacheHint;
|
|
33631
33683
|
this.setResourceRequestHandlers();
|
|
33632
33684
|
this.sendResourceListChanged();
|
|
33633
33685
|
return registeredResourceTemplate;
|
|
33634
33686
|
}
|
|
33635
33687
|
}
|
|
33636
|
-
_createRegisteredResource(name, title, uri, metadata, readCallback) {
|
|
33688
|
+
_createRegisteredResource(name, title, uri, metadata, scopeChallenge, readCallback) {
|
|
33637
33689
|
const registeredResource = {
|
|
33638
33690
|
name,
|
|
33639
33691
|
title,
|
|
33640
33692
|
metadata,
|
|
33641
33693
|
readCallback,
|
|
33694
|
+
scopeChallenge,
|
|
33642
33695
|
enabled: true,
|
|
33643
33696
|
disable: () => registeredResource.update({ enabled: false }),
|
|
33644
33697
|
enable: () => registeredResource.update({ enabled: true }),
|
|
@@ -33652,6 +33705,7 @@ var McpServer = class {
|
|
|
33652
33705
|
if (updates.title !== void 0) registeredResource.title = updates.title;
|
|
33653
33706
|
if (updates.metadata !== void 0) registeredResource.metadata = updates.metadata;
|
|
33654
33707
|
if (updates.callback !== void 0) registeredResource.readCallback = updates.callback;
|
|
33708
|
+
if (updates.scopeChallenge !== void 0) registeredResource.scopeChallenge = updates.scopeChallenge === null ? void 0 : updates.scopeChallenge;
|
|
33655
33709
|
if (updates.enabled !== void 0) registeredResource.enabled = updates.enabled;
|
|
33656
33710
|
this.sendResourceListChanged();
|
|
33657
33711
|
}
|
|
@@ -33659,12 +33713,13 @@ var McpServer = class {
|
|
|
33659
33713
|
this._registeredResources[uri] = registeredResource;
|
|
33660
33714
|
return registeredResource;
|
|
33661
33715
|
}
|
|
33662
|
-
_createRegisteredResourceTemplate(name, title, template, metadata, readCallback) {
|
|
33716
|
+
_createRegisteredResourceTemplate(name, title, template, metadata, scopeChallenge, readCallback) {
|
|
33663
33717
|
const registeredResourceTemplate = {
|
|
33664
33718
|
resourceTemplate: template,
|
|
33665
33719
|
title,
|
|
33666
33720
|
metadata,
|
|
33667
33721
|
readCallback,
|
|
33722
|
+
scopeChallenge,
|
|
33668
33723
|
enabled: true,
|
|
33669
33724
|
disable: () => registeredResourceTemplate.update({ enabled: false }),
|
|
33670
33725
|
enable: () => registeredResourceTemplate.update({ enabled: true }),
|
|
@@ -33678,6 +33733,7 @@ var McpServer = class {
|
|
|
33678
33733
|
if (updates.template !== void 0) registeredResourceTemplate.resourceTemplate = updates.template;
|
|
33679
33734
|
if (updates.metadata !== void 0) registeredResourceTemplate.metadata = updates.metadata;
|
|
33680
33735
|
if (updates.callback !== void 0) registeredResourceTemplate.readCallback = updates.callback;
|
|
33736
|
+
if (updates.scopeChallenge !== void 0) registeredResourceTemplate.scopeChallenge = updates.scopeChallenge === null ? void 0 : updates.scopeChallenge;
|
|
33681
33737
|
if (updates.enabled !== void 0) registeredResourceTemplate.enabled = updates.enabled;
|
|
33682
33738
|
this.sendResourceListChanged();
|
|
33683
33739
|
}
|
|
@@ -33687,7 +33743,7 @@ var McpServer = class {
|
|
|
33687
33743
|
if (Array.isArray(variableNames) && variableNames.some((v) => !!template.completeCallback(v))) this.setCompletionRequestHandler();
|
|
33688
33744
|
return registeredResourceTemplate;
|
|
33689
33745
|
}
|
|
33690
|
-
_createRegisteredPrompt(name, title, description, argsSchema, callback, icons, _meta) {
|
|
33746
|
+
_createRegisteredPrompt(name, title, description, argsSchema, callback, icons, scopeChallenge, _meta) {
|
|
33691
33747
|
let currentArgsSchema = argsSchema;
|
|
33692
33748
|
let currentCallback = callback;
|
|
33693
33749
|
const registeredPrompt = {
|
|
@@ -33695,6 +33751,7 @@ var McpServer = class {
|
|
|
33695
33751
|
description,
|
|
33696
33752
|
argsSchema,
|
|
33697
33753
|
icons,
|
|
33754
|
+
scopeChallenge,
|
|
33698
33755
|
_meta,
|
|
33699
33756
|
handler: createPromptHandler(name, argsSchema, callback),
|
|
33700
33757
|
enabled: true,
|
|
@@ -33709,6 +33766,7 @@ var McpServer = class {
|
|
|
33709
33766
|
if (updates.title !== void 0) registeredPrompt.title = updates.title;
|
|
33710
33767
|
if (updates.description !== void 0) registeredPrompt.description = updates.description;
|
|
33711
33768
|
if (updates.icons !== void 0) registeredPrompt.icons = updates.icons;
|
|
33769
|
+
if (updates.scopeChallenge !== void 0) registeredPrompt.scopeChallenge = updates.scopeChallenge === null ? void 0 : updates.scopeChallenge;
|
|
33712
33770
|
if (updates._meta !== void 0) registeredPrompt._meta = updates._meta;
|
|
33713
33771
|
let needsHandlerRegen = false;
|
|
33714
33772
|
if (updates.argsSchema !== void 0) {
|
|
@@ -33736,7 +33794,7 @@ var McpServer = class {
|
|
|
33736
33794
|
}
|
|
33737
33795
|
return registeredPrompt;
|
|
33738
33796
|
}
|
|
33739
|
-
_createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, icons, execution, _meta, handler) {
|
|
33797
|
+
_createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, icons, execution, scopeChallenge, _meta, handler) {
|
|
33740
33798
|
validateAndWarnToolName(name);
|
|
33741
33799
|
if (inputSchema !== void 0) try {
|
|
33742
33800
|
const json2 = standardSchemaToJsonSchema(inputSchema, "input");
|
|
@@ -33755,6 +33813,7 @@ var McpServer = class {
|
|
|
33755
33813
|
annotations,
|
|
33756
33814
|
icons,
|
|
33757
33815
|
execution,
|
|
33816
|
+
scopeChallenge,
|
|
33758
33817
|
_meta,
|
|
33759
33818
|
handler,
|
|
33760
33819
|
executor: createToolExecutor(inputSchema, handler),
|
|
@@ -33793,6 +33852,7 @@ var McpServer = class {
|
|
|
33793
33852
|
}
|
|
33794
33853
|
if (updates.annotations !== void 0) registeredTool.annotations = updates.annotations;
|
|
33795
33854
|
if (updates.icons !== void 0) registeredTool.icons = updates.icons;
|
|
33855
|
+
if (updates.scopeChallenge !== void 0) registeredTool.scopeChallenge = updates.scopeChallenge === null ? void 0 : updates.scopeChallenge;
|
|
33796
33856
|
if (updates._meta !== void 0) registeredTool._meta = updates._meta;
|
|
33797
33857
|
if (updates.enabled !== void 0) registeredTool.enabled = updates.enabled;
|
|
33798
33858
|
this.sendToolListChanged();
|
|
@@ -33805,13 +33865,13 @@ var McpServer = class {
|
|
|
33805
33865
|
}
|
|
33806
33866
|
registerTool(name, config2, cb) {
|
|
33807
33867
|
if (this._registeredTools[name]) throw new Error(`Tool ${name} is already registered`);
|
|
33808
|
-
const { title, description, inputSchema, outputSchema, annotations, icons, _meta } = config2;
|
|
33809
|
-
return this._createRegisteredTool(name, title, description, normalizeRawShapeSchema(inputSchema), normalizeRawShapeSchema(outputSchema), annotations, icons, void 0, _meta, cb);
|
|
33868
|
+
const { title, description, inputSchema, outputSchema, annotations, icons, scopeChallenge, _meta } = config2;
|
|
33869
|
+
return this._createRegisteredTool(name, title, description, normalizeRawShapeSchema(inputSchema), normalizeRawShapeSchema(outputSchema), annotations, icons, void 0, scopeChallenge, _meta, cb);
|
|
33810
33870
|
}
|
|
33811
33871
|
registerPrompt(name, config2, cb) {
|
|
33812
33872
|
if (this._registeredPrompts[name]) throw new Error(`Prompt ${name} is already registered`);
|
|
33813
|
-
const { title, description, argsSchema, icons, _meta } = config2;
|
|
33814
|
-
const registeredPrompt = this._createRegisteredPrompt(name, title, description, normalizeRawShapeSchema(argsSchema), cb, icons, _meta);
|
|
33873
|
+
const { title, description, argsSchema, icons, scopeChallenge, _meta } = config2;
|
|
33874
|
+
const registeredPrompt = this._createRegisteredPrompt(name, title, description, normalizeRawShapeSchema(argsSchema), cb, icons, scopeChallenge, _meta);
|
|
33815
33875
|
this.setPromptRequestHandlers();
|
|
33816
33876
|
this.sendPromptListChanged();
|
|
33817
33877
|
return registeredPrompt;
|
|
@@ -33923,6 +33983,7 @@ function unwrapOptionalSchema(schema) {
|
|
|
33923
33983
|
}
|
|
33924
33984
|
|
|
33925
33985
|
// ../../node_modules/@modelcontextprotocol/server/dist/stdio.mjs
|
|
33986
|
+
var swallowsErrorsAfterClose = /* @__PURE__ */ Symbol("swallowsErrorsAfterClose");
|
|
33926
33987
|
var StdioServerTransport = class {
|
|
33927
33988
|
_readBuffer;
|
|
33928
33989
|
_started = false;
|
|
@@ -33949,18 +34010,27 @@ var StdioServerTransport = class {
|
|
|
33949
34010
|
this.onerror?.(error62);
|
|
33950
34011
|
};
|
|
33951
34012
|
_onstdouterror = (error62) => {
|
|
34013
|
+
if (this._closed) return;
|
|
33952
34014
|
this.onerror?.(error62);
|
|
33953
34015
|
this.close().catch(() => {
|
|
33954
34016
|
});
|
|
33955
34017
|
};
|
|
34018
|
+
_onstdinclose = () => {
|
|
34019
|
+
this.close().catch(() => {
|
|
34020
|
+
});
|
|
34021
|
+
};
|
|
33956
34022
|
/**
|
|
33957
34023
|
* Starts listening for messages on `stdin`.
|
|
33958
34024
|
*/
|
|
33959
34025
|
async start() {
|
|
33960
34026
|
if (this._started) throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
|
|
33961
34027
|
this._started = true;
|
|
34028
|
+
for (const listener of this._stdout.listeners("error")) if (listener[swallowsErrorsAfterClose]) this._stdout.off("error", listener);
|
|
34029
|
+
if (this._stdin.readableEnded || this._stdin.destroyed) setImmediate(this._onstdinclose);
|
|
33962
34030
|
this._stdin.on("data", this._ondata);
|
|
33963
34031
|
this._stdin.on("error", this._onerror);
|
|
34032
|
+
this._stdin.on("end", this._onstdinclose);
|
|
34033
|
+
this._stdin.on("close", this._onstdinclose);
|
|
33964
34034
|
this._stdout.on("error", this._onstdouterror);
|
|
33965
34035
|
}
|
|
33966
34036
|
processReadBuffer() {
|
|
@@ -33977,7 +34047,9 @@ var StdioServerTransport = class {
|
|
|
33977
34047
|
this._closed = true;
|
|
33978
34048
|
this._stdin.off("data", this._ondata);
|
|
33979
34049
|
this._stdin.off("error", this._onerror);
|
|
33980
|
-
this.
|
|
34050
|
+
this._stdin.off("end", this._onstdinclose);
|
|
34051
|
+
this._stdin.off("close", this._onstdinclose);
|
|
34052
|
+
this._onstdouterror[swallowsErrorsAfterClose] = true;
|
|
33981
34053
|
if (this._stdin.listenerCount("data") === 0) this._stdin.pause();
|
|
33982
34054
|
this._readBuffer.clear();
|
|
33983
34055
|
this.onclose?.();
|
|
@@ -34254,6 +34326,7 @@ function serveStdio(factory, options = {}) {
|
|
|
34254
34326
|
return;
|
|
34255
34327
|
}
|
|
34256
34328
|
if (state.era === "modern" && await tryServeListen(message)) return;
|
|
34329
|
+
if (isTornDown()) return;
|
|
34257
34330
|
state.instance.channel.deliver(message);
|
|
34258
34331
|
return;
|
|
34259
34332
|
}
|
|
@@ -34320,6 +34393,7 @@ function serveStdio(factory, options = {}) {
|
|
|
34320
34393
|
};
|
|
34321
34394
|
}
|
|
34322
34395
|
if (await tryServeListen(message)) return;
|
|
34396
|
+
if (isTornDown()) return;
|
|
34323
34397
|
state.instance.channel.deliver(message, { classification: opening.classification });
|
|
34324
34398
|
return;
|
|
34325
34399
|
case "legacy": {
|
|
@@ -34406,6 +34480,9 @@ function toError(value) {
|
|
|
34406
34480
|
return value instanceof Error ? value : new Error(String(value));
|
|
34407
34481
|
}
|
|
34408
34482
|
|
|
34483
|
+
// ../../node_modules/@modelcontextprotocol/server/dist/index.mjs
|
|
34484
|
+
var DEFAULT_MAX_REQUEST_BODY_SIZE = 4 * 1024 * 1024;
|
|
34485
|
+
|
|
34409
34486
|
// ../../node_modules/@chrischall/mcp-utils/dist/errors/index.js
|
|
34410
34487
|
var McpToolError = class extends Error {
|
|
34411
34488
|
/** Actionable remediation text, when one applies. */
|
|
@@ -35971,6 +36048,7 @@ async function diagnose(err) {
|
|
|
35971
36048
|
const isAuthError = !isTransientError && AUTH_ERROR_PATTERN.test(errText);
|
|
35972
36049
|
const isGridLimitError = GRID_LIMIT_ERROR_PATTERN.test(errText);
|
|
35973
36050
|
const hint = isInvalidGrant ? INVALID_GRANT_HINT : isAuthError ? AUTH_HINT : isTransientError ? TRANSIENT_HINT : isGridLimitError ? GRID_LIMIT_HINT : "";
|
|
36051
|
+
if (!isAuthError && !isInvalidGrant) return errorResult(`${errText}${hint}`);
|
|
35974
36052
|
try {
|
|
35975
36053
|
const accounts = formatAccountList(await run(["auth", "list"]));
|
|
35976
36054
|
return errorResult(`${errText}
|
|
@@ -36115,12 +36193,53 @@ function attachmentPreview(details) {
|
|
|
36115
36193
|
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
36194
|
var CONFIRM_ACTION_INSTRUCTION = CONFIRM_TOKEN_INSTRUCTION;
|
|
36117
36195
|
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.';
|
|
36196
|
+
function refusedInRun(what, via, does, instead) {
|
|
36197
|
+
return `${what} ${does} and is not available through ${via}. ${instead}`;
|
|
36198
|
+
}
|
|
36118
36199
|
function gatedElsewhere(what, via, does, tool) {
|
|
36119
|
-
return
|
|
36200
|
+
return refusedInRun(what, via, does, `Use ${tool}, which asks the user to confirm.`);
|
|
36120
36201
|
}
|
|
36121
36202
|
function hasCommandWord(args, words) {
|
|
36122
36203
|
return args.find((a) => words.has(a.toLowerCase()));
|
|
36123
36204
|
}
|
|
36205
|
+
function flagValue(args, name) {
|
|
36206
|
+
const flag = `--${name}`;
|
|
36207
|
+
for (let i = 0; i < args.length; i++) {
|
|
36208
|
+
const a = args[i];
|
|
36209
|
+
if (a === flag) return args[i + 1] ?? "";
|
|
36210
|
+
if (a.startsWith(`${flag}=`)) return a.slice(flag.length + 1);
|
|
36211
|
+
}
|
|
36212
|
+
return void 0;
|
|
36213
|
+
}
|
|
36214
|
+
var COMMENT_ADD_WORDS = /* @__PURE__ */ new Set(["create", "add", "new"]);
|
|
36215
|
+
var COMMENT_REPLY_WORDS = /* @__PURE__ */ new Set(["reply", "respond"]);
|
|
36216
|
+
function hasTrueFlag(args, flag) {
|
|
36217
|
+
const name = `--${flag}`;
|
|
36218
|
+
return args.some((a) => {
|
|
36219
|
+
const lower = a.toLowerCase();
|
|
36220
|
+
if (lower === name) return true;
|
|
36221
|
+
return lower.startsWith(`${name}=`) && lower.slice(name.length + 1) !== "false";
|
|
36222
|
+
});
|
|
36223
|
+
}
|
|
36224
|
+
function vetCommentsRun(service, subcommand, args) {
|
|
36225
|
+
if (subcommand.toLowerCase() !== "comments") return void 0;
|
|
36226
|
+
const reply = hasCommandWord(args, COMMENT_REPLY_WORDS);
|
|
36227
|
+
if (reply) {
|
|
36228
|
+
return gatedElsewhere(
|
|
36229
|
+
`gog ${service} comments ${reply.toLowerCase()}`,
|
|
36230
|
+
`gog_${service}_run`,
|
|
36231
|
+
"notifies the comment thread",
|
|
36232
|
+
`gog_${service}_comments_reply`
|
|
36233
|
+
);
|
|
36234
|
+
}
|
|
36235
|
+
const add = hasCommandWord(args, COMMENT_ADD_WORDS);
|
|
36236
|
+
return add ? gatedElsewhere(
|
|
36237
|
+
`gog ${service} comments ${add.toLowerCase()}`,
|
|
36238
|
+
`gog_${service}_run`,
|
|
36239
|
+
"notifies the file's owner and anyone it mentions",
|
|
36240
|
+
`gog_${service}_comments_add`
|
|
36241
|
+
) : void 0;
|
|
36242
|
+
}
|
|
36124
36243
|
async function requireDispatchConfirmation(ctx, options) {
|
|
36125
36244
|
const { action, fallback } = options;
|
|
36126
36245
|
const confirmation = {
|
|
@@ -36149,16 +36268,33 @@ function resultText(result) {
|
|
|
36149
36268
|
// src/tools/api.ts
|
|
36150
36269
|
var GMAIL_API_BLOCKED = /(?:\.send$|forwarding|filters\.create|filters\.update|delegates\.create)/i;
|
|
36151
36270
|
var DISPATCH_API_BLOCKED = {
|
|
36152
|
-
chat: [
|
|
36153
|
-
|
|
36271
|
+
chat: [
|
|
36272
|
+
{ method: /(?:^|\.)spaces\.messages\.create$/i, does: "posts a Chat message", tool: "gog_chat_messages_send / gog_chat_dm_send" },
|
|
36273
|
+
{ method: /(?:^|\.)spaces\.(?:setup|members\.create)$/i, does: "adds people to a space", tool: "gog_chat_spaces_create" }
|
|
36274
|
+
],
|
|
36275
|
+
drive: [
|
|
36276
|
+
{ method: /(?:^|\.)permissions\.(?:create|update)$/i, does: "grants access to a file", tool: "gog_drive_share" },
|
|
36277
|
+
{ method: /(?:^|\.)comments\.create$/i, does: "notifies the file's owner and anyone it mentions", tool: "gog_drive_comments_add / gog_docs_comments_add" },
|
|
36278
|
+
{ method: /(?:^|\.)replies\.create$/i, does: "notifies the comment thread", tool: "gog_drive_comments_reply / gog_docs_comments_reply" }
|
|
36279
|
+
],
|
|
36154
36280
|
classroom: [
|
|
36155
36281
|
{ 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" }
|
|
36282
|
+
{ method: /(?:^|\.)invitations\.create$/i, does: "invites someone to a class", tool: "gog_classroom_invitations_create" },
|
|
36283
|
+
{ method: /(?:^|\.)courses\.students\.create$/i, does: "adds someone to a class", tool: "gog_classroom_students_add" },
|
|
36284
|
+
{ method: /(?:^|\.)courses\.teachers\.create$/i, does: "gives someone teacher access to a class", tool: "gog_classroom_teachers_add" },
|
|
36285
|
+
{ method: /(?:^|\.)courses\.coursework\.create$/i, does: "posts coursework to a class", tool: "gog_classroom_coursework_create" },
|
|
36286
|
+
{ method: /(?:^|\.)studentsubmissions\.return$/i, does: "returns work to a student", tool: "gog_classroom_submissions_return" }
|
|
36157
36287
|
],
|
|
36158
36288
|
calendar: [
|
|
36159
36289
|
{ 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
|
-
|
|
36290
|
+
{ method: /(?:^|\.)events\.(?:update|patch)$/i, does: "can change what guests see", tool: "gog_calendar_update / gog_calendar_respond" },
|
|
36291
|
+
{ method: /(?:^|\.)events\.move$/i, does: "can email every guest", tool: "gog_calendar_move" }
|
|
36292
|
+
],
|
|
36293
|
+
gmail: [
|
|
36294
|
+
{ method: /(?:^|\.)settings\.sendas\.create$/i, does: "makes Google email the address and adds a sending identity", tool: "gog_gmail_sendas_create" },
|
|
36295
|
+
{ method: /(?:^|\.)settings\.updatevacation$/i, does: "can turn on an auto-reply to every sender", tool: "gog_gmail_vacation_update" }
|
|
36296
|
+
],
|
|
36297
|
+
script: [{ method: /(?:^|\.)scripts\.run$/i, does: "executes code with this account's authority", tool: "gog_appscript_run_function" }]
|
|
36162
36298
|
};
|
|
36163
36299
|
function refusedApiCall(api, method) {
|
|
36164
36300
|
const name = api.trim().toLowerCase();
|
|
@@ -36169,6 +36305,14 @@ function refusedApiCall(api, method) {
|
|
|
36169
36305
|
const hit = (Object.hasOwn(DISPATCH_API_BLOCKED, name) ? DISPATCH_API_BLOCKED[name] : []).find((b) => b.method.test(m));
|
|
36170
36306
|
return hit ? gatedElsewhere(`${name} ${m}`, "gog_api_call", hit.does, hit.tool) : void 0;
|
|
36171
36307
|
}
|
|
36308
|
+
function previewJson(text) {
|
|
36309
|
+
if (text.length > BODY_PREVIEW_MAX) return bodyPreview(text);
|
|
36310
|
+
try {
|
|
36311
|
+
return JSON.parse(text);
|
|
36312
|
+
} catch {
|
|
36313
|
+
return text;
|
|
36314
|
+
}
|
|
36315
|
+
}
|
|
36172
36316
|
function registerApiTools(server) {
|
|
36173
36317
|
server.registerTool("gog_api_list", {
|
|
36174
36318
|
description: "List the Google Discovery APIs available for gog_api_call / gog_api_describe (name + version + title).",
|
|
@@ -36197,7 +36341,7 @@ function registerApiTools(server) {
|
|
|
36197
36341
|
return runOrDiagnose(args, { account });
|
|
36198
36342
|
});
|
|
36199
36343
|
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
|
|
36344
|
+
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
36345
|
annotations: { destructiveHint: true },
|
|
36202
36346
|
inputSchema: external_exports.object({
|
|
36203
36347
|
api: external_exports.string().describe("Discovery API name (e.g. drive, gmail, calendar)"),
|
|
@@ -36206,11 +36350,12 @@ function registerApiTools(server) {
|
|
|
36206
36350
|
params: external_exports.string().optional().describe('Query/path parameters as a JSON object string (e.g. {"fileId":"abc","fields":"name"})'),
|
|
36207
36351
|
body: external_exports.string().optional().describe("Request body as a JSON string (for write methods)"),
|
|
36208
36352
|
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
|
|
36353
|
+
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."),
|
|
36354
|
+
dryRun: external_exports.boolean().optional().describe("Print the intended request and exit without sending it (no changes made, no confirmation prompt)"),
|
|
36355
|
+
account: accountParam,
|
|
36356
|
+
confirmToken: confirmTokenParam
|
|
36212
36357
|
})
|
|
36213
|
-
}, async ({ api, version: version2, method, params, body, scope, allowWrite, dryRun, account }) => {
|
|
36358
|
+
}, async ({ api, version: version2, method, params, body, scope, allowWrite, dryRun, account, confirmToken }, ctx) => {
|
|
36214
36359
|
try {
|
|
36215
36360
|
assertSafeForwardedArgs([api, version2, method]);
|
|
36216
36361
|
const refusal = refusedApiCall(api, method);
|
|
@@ -36220,6 +36365,27 @@ function registerApiTools(server) {
|
|
|
36220
36365
|
} catch (err) {
|
|
36221
36366
|
return errorResult(errorText(err));
|
|
36222
36367
|
}
|
|
36368
|
+
if (allowWrite && !dryRun) {
|
|
36369
|
+
const request = { api, version: version2, method, params, body, scope };
|
|
36370
|
+
const view = { api, version: version2, method };
|
|
36371
|
+
if (params !== void 0) view.params = previewJson(params);
|
|
36372
|
+
if (body !== void 0) view.body = previewJson(body);
|
|
36373
|
+
if (scope !== void 0) view.scope = scope;
|
|
36374
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
36375
|
+
action: "api.call",
|
|
36376
|
+
message: `Review and confirm this raw Google API write (${api} ${version2} ${method}):`,
|
|
36377
|
+
confirmationLabel: "Confirm that this API method should be invoked with exactly these parameters.",
|
|
36378
|
+
details: view,
|
|
36379
|
+
unsupportedNote: "Set dryRun=true to see the request gog would send without sending it, or use the dedicated gog_* tool for this operation.",
|
|
36380
|
+
fallback: {
|
|
36381
|
+
tool: "gog_api_call",
|
|
36382
|
+
account,
|
|
36383
|
+
confirmToken,
|
|
36384
|
+
subject: () => ({ target: `${api}/${version2}/${method}`, payload: request, preview: view })
|
|
36385
|
+
}
|
|
36386
|
+
});
|
|
36387
|
+
if (confirmation) return confirmation;
|
|
36388
|
+
}
|
|
36223
36389
|
const args = ["api", "call", pos(api), pos(version2), pos(method)];
|
|
36224
36390
|
if (params) args.push(`--params=${params}`);
|
|
36225
36391
|
if (body) args.push(`--body=${body}`);
|
|
@@ -36232,6 +36398,22 @@ function registerApiTools(server) {
|
|
|
36232
36398
|
}
|
|
36233
36399
|
|
|
36234
36400
|
// src/tools/appscript.ts
|
|
36401
|
+
function vetAppScriptRun(subcommand, _args) {
|
|
36402
|
+
return subcommand.toLowerCase() === "run" ? gatedElsewhere("gog appscript run", "gog_appscript_run", "executes code with this account's authority", "gog_appscript_run_function") : void 0;
|
|
36403
|
+
}
|
|
36404
|
+
function projectSnapshot(raw, scriptId) {
|
|
36405
|
+
let project;
|
|
36406
|
+
try {
|
|
36407
|
+
project = JSON.parse(raw)?.project;
|
|
36408
|
+
} catch {
|
|
36409
|
+
project = void 0;
|
|
36410
|
+
}
|
|
36411
|
+
return {
|
|
36412
|
+
scriptId,
|
|
36413
|
+
...typeof project?.title === "string" ? { title: project.title } : {},
|
|
36414
|
+
...typeof project?.updateTime === "string" ? { updateTime: project.updateTime } : {}
|
|
36415
|
+
};
|
|
36416
|
+
}
|
|
36235
36417
|
function registerAppScriptTools(server) {
|
|
36236
36418
|
const scriptIdParam = external_exports.string().describe(
|
|
36237
36419
|
"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 +36494,18 @@ function registerAppScriptTools(server) {
|
|
|
36312
36494
|
return runOrDiagnose(args, { account });
|
|
36313
36495
|
});
|
|
36314
36496
|
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,
|
|
36497
|
+
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
36498
|
annotations: { destructiveHint: true },
|
|
36317
36499
|
inputSchema: external_exports.object({
|
|
36318
36500
|
scriptId: scriptIdParam,
|
|
36319
36501
|
functionName: external_exports.string().describe('Name of the function to call, e.g. "doWork"'),
|
|
36320
36502
|
params: external_exports.string().optional().describe(`Function parameters as a JSON ARRAY of positional arguments, e.g. '["a", 1]' \u2014 not an object`),
|
|
36321
36503
|
devMode: external_exports.boolean().optional().describe("Run the latest saved code rather than the deployed version (owner only)"),
|
|
36322
|
-
account: accountParam
|
|
36504
|
+
account: accountParam,
|
|
36505
|
+
confirmToken: confirmTokenParam
|
|
36323
36506
|
})
|
|
36324
|
-
}, async ({ scriptId, functionName, params, devMode, account }) => {
|
|
36507
|
+
}, async ({ scriptId, functionName, params, devMode, account, confirmToken }, ctx) => {
|
|
36508
|
+
let parsedParams;
|
|
36325
36509
|
if (params !== void 0) {
|
|
36326
36510
|
let parsed;
|
|
36327
36511
|
try {
|
|
@@ -36332,7 +36516,26 @@ function registerAppScriptTools(server) {
|
|
|
36332
36516
|
if (!Array.isArray(parsed)) {
|
|
36333
36517
|
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
36518
|
}
|
|
36519
|
+
parsedParams = parsed;
|
|
36335
36520
|
}
|
|
36521
|
+
const got = await runOrDiagnose(["appscript", "get", pos(scriptId)], { account });
|
|
36522
|
+
if (got.isError) return got;
|
|
36523
|
+
const { updateTime, ...project } = projectSnapshot(resultText(got), scriptId);
|
|
36524
|
+
const execution = { project, functionName, params: parsedParams, devMode: Boolean(devMode), runsAs: senderPreview(account) };
|
|
36525
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
36526
|
+
action: "appscript.run-function",
|
|
36527
|
+
message: "Review and confirm running this Apps Script function with the account's full authority:",
|
|
36528
|
+
confirmationLabel: "Confirm that this code should run now.",
|
|
36529
|
+
details: execution,
|
|
36530
|
+
unsupportedNote: "Ask the user to run it themselves from the Apps Script editor.",
|
|
36531
|
+
fallback: {
|
|
36532
|
+
tool: "gog_appscript_run_function",
|
|
36533
|
+
account,
|
|
36534
|
+
confirmToken,
|
|
36535
|
+
subject: () => ({ target: `${scriptId}/${functionName}`, revision: updateTime, payload: execution, preview: execution })
|
|
36536
|
+
}
|
|
36537
|
+
});
|
|
36538
|
+
if (confirmation) return confirmation;
|
|
36336
36539
|
const args = ["appscript", "run", pos(scriptId), pos(functionName)];
|
|
36337
36540
|
if (params !== void 0) args.push(`--params=${params}`);
|
|
36338
36541
|
if (devMode) args.push("--dev-mode");
|
|
@@ -36341,6 +36544,7 @@ function registerAppScriptTools(server) {
|
|
|
36341
36544
|
registerRunTool(server, {
|
|
36342
36545
|
service: "appscript",
|
|
36343
36546
|
examples: '"get", "content", "deployments"',
|
|
36547
|
+
vet: vetAppScriptRun,
|
|
36344
36548
|
note: "To execute a function, use gog_appscript_run_function \u2014 this tool is the generic escape hatch."
|
|
36345
36549
|
});
|
|
36346
36550
|
}
|
|
@@ -36480,21 +36684,47 @@ function registerAuthTools(server) {
|
|
|
36480
36684
|
}
|
|
36481
36685
|
|
|
36482
36686
|
// src/tools/calendar.ts
|
|
36483
|
-
var
|
|
36484
|
-
|
|
36485
|
-
|
|
36486
|
-
|
|
36487
|
-
|
|
36488
|
-
|
|
36489
|
-
|
|
36490
|
-
|
|
36491
|
-
|
|
36492
|
-
|
|
36687
|
+
var ASKS = (tool) => `Use ${tool}, which asks the user to confirm.`;
|
|
36688
|
+
var CALENDAR_REFUSED = {
|
|
36689
|
+
create: { does: "can change what guests see", instead: ASKS("gog_calendar_create") },
|
|
36690
|
+
add: { does: "can change what guests see", instead: ASKS("gog_calendar_create") },
|
|
36691
|
+
new: { does: "can change what guests see", instead: ASKS("gog_calendar_create") },
|
|
36692
|
+
update: { does: "can change what guests see", instead: ASKS("gog_calendar_update") },
|
|
36693
|
+
edit: { does: "can change what guests see", instead: ASKS("gog_calendar_update") },
|
|
36694
|
+
set: { does: "can change what guests see", instead: ASKS("gog_calendar_update") },
|
|
36695
|
+
respond: { does: "can change what guests see", instead: ASKS("gog_calendar_respond") },
|
|
36696
|
+
rsvp: { does: "can change what guests see", instead: ASKS("gog_calendar_respond") },
|
|
36697
|
+
reply: { does: "can change what guests see", instead: ASKS("gog_calendar_respond") },
|
|
36698
|
+
move: { does: "moves an event off guests' calendars (and can email them)", instead: ASKS("gog_calendar_move") },
|
|
36699
|
+
transfer: { does: "moves an event off guests' calendars (and can email them)", instead: ASKS("gog_calendar_move") },
|
|
36700
|
+
delete: { does: "removes an event from every guest's calendar (the whole series by default)", instead: ASKS("gog_calendar_delete") },
|
|
36701
|
+
del: { does: "removes an event from every guest's calendar (the whole series by default)", instead: ASKS("gog_calendar_delete") },
|
|
36702
|
+
rm: { does: "removes an event from every guest's calendar (the whole series by default)", instead: ASKS("gog_calendar_delete") },
|
|
36703
|
+
remove: { does: "removes an event from every guest's calendar (the whole series by default)", instead: ASKS("gog_calendar_delete") },
|
|
36704
|
+
"delete-calendar": { does: "deletes a calendar and every event on it", instead: ASKS("gog_calendar_delete_calendar") },
|
|
36705
|
+
"out-of-office": { does: "auto-declines invitations and notifies their organizers", instead: ASKS("gog_calendar_out_of_office") },
|
|
36706
|
+
ooo: { does: "auto-declines invitations and notifies their organizers", instead: ASKS("gog_calendar_out_of_office") }
|
|
36493
36707
|
};
|
|
36494
|
-
function vetCalendarRun(subcommand,
|
|
36708
|
+
function vetCalendarRun(subcommand, args) {
|
|
36495
36709
|
const sub = subcommand.toLowerCase();
|
|
36496
|
-
const
|
|
36497
|
-
|
|
36710
|
+
const what = `gog calendar ${sub}`;
|
|
36711
|
+
const via = "gog_calendar_run";
|
|
36712
|
+
if (Object.hasOwn(CALENDAR_REFUSED, sub)) {
|
|
36713
|
+
const { does, instead } = CALENDAR_REFUSED[sub];
|
|
36714
|
+
return refusedInRun(what, via, does, instead);
|
|
36715
|
+
}
|
|
36716
|
+
if (sub === "propose-time" && (flagValue(args, "decline") !== void 0 || flagValue(args, "comment") !== void 0)) {
|
|
36717
|
+
return gatedElsewhere(`${what} --decline`, via, "declines the event and notifies the organizer", "gog_calendar_respond");
|
|
36718
|
+
}
|
|
36719
|
+
if ((sub === "focus-time" || sub === "focus") && flagValue(args, "auto-decline")?.toLowerCase() !== "none") {
|
|
36720
|
+
return refusedInRun(
|
|
36721
|
+
what,
|
|
36722
|
+
via,
|
|
36723
|
+
"auto-declines invitations and notifies their organizers (gog defaults --auto-decline to all)",
|
|
36724
|
+
"Pass --auto-decline=none, or ask the user to create it from Google Calendar."
|
|
36725
|
+
);
|
|
36726
|
+
}
|
|
36727
|
+
return void 0;
|
|
36498
36728
|
}
|
|
36499
36729
|
function parseAttendees(value) {
|
|
36500
36730
|
if (!value) return [];
|
|
@@ -36511,15 +36741,17 @@ function eventSnapshot(raw) {
|
|
|
36511
36741
|
} catch {
|
|
36512
36742
|
event = void 0;
|
|
36513
36743
|
}
|
|
36514
|
-
const
|
|
36744
|
+
const str2 = (v) => typeof v === "string" ? v : void 0;
|
|
36515
36745
|
return {
|
|
36516
|
-
summary:
|
|
36746
|
+
summary: str2(event?.summary),
|
|
36517
36747
|
start: timeOf(event?.start),
|
|
36518
36748
|
end: timeOf(event?.end),
|
|
36519
|
-
organizer:
|
|
36749
|
+
organizer: str2(event?.organizer?.email),
|
|
36520
36750
|
// Guests are the OTHER people: not this account's own attendee row, not rooms.
|
|
36521
36751
|
guests: (Array.isArray(event?.attendees) ? event.attendees : []).filter((a) => a.self !== true && a.resource !== true && typeof a.email === "string").map((a) => a.email),
|
|
36522
|
-
|
|
36752
|
+
// A series, or one instance of one: gog's delete --scope defaults to all.
|
|
36753
|
+
...Array.isArray(event?.recurrence) && event.recurrence.length > 0 || typeof event?.recurringEventId === "string" ? { recurring: true } : {},
|
|
36754
|
+
etag: str2(event?.etag)
|
|
36523
36755
|
};
|
|
36524
36756
|
}
|
|
36525
36757
|
async function readEvent(calendarId, eventId, account) {
|
|
@@ -36740,14 +36972,35 @@ function registerCalendarTools(server) {
|
|
|
36740
36972
|
return runOrDiagnose(args, { account });
|
|
36741
36973
|
});
|
|
36742
36974
|
server.registerTool("gog_calendar_delete", {
|
|
36743
|
-
description: "Delete a calendar event.",
|
|
36975
|
+
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
36976
|
annotations: { destructiveHint: true },
|
|
36745
36977
|
inputSchema: external_exports.object({
|
|
36746
36978
|
calendarId: external_exports.string().describe("Calendar ID"),
|
|
36747
36979
|
eventId: external_exports.string().describe("Event ID"),
|
|
36748
|
-
account: accountParam
|
|
36980
|
+
account: accountParam,
|
|
36981
|
+
confirmToken: confirmTokenParam
|
|
36749
36982
|
})
|
|
36750
|
-
}, async ({ calendarId, eventId, account }) => {
|
|
36983
|
+
}, async ({ calendarId, eventId, account, confirmToken }, ctx) => {
|
|
36984
|
+
const read = await readEvent(calendarId, eventId, account);
|
|
36985
|
+
if (read.error) return read.error;
|
|
36986
|
+
const { etag, ...event } = read.event;
|
|
36987
|
+
if (event.guests.length > 0 || event.recurring) {
|
|
36988
|
+
const view = { calendarId, eventId, event, scope: event.recurring ? "the whole recurring series" : "this event" };
|
|
36989
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
36990
|
+
action: "calendar.delete",
|
|
36991
|
+
message: event.recurring ? "Review and confirm deleting this ENTIRE recurring series:" : "Review and confirm deleting this event from every guest's calendar:",
|
|
36992
|
+
confirmationLabel: "Confirm that this event should be deleted now.",
|
|
36993
|
+
details: view,
|
|
36994
|
+
unsupportedNote: "Ask the user to delete it from Google Calendar.",
|
|
36995
|
+
fallback: {
|
|
36996
|
+
tool: "gog_calendar_delete",
|
|
36997
|
+
account,
|
|
36998
|
+
confirmToken,
|
|
36999
|
+
subject: () => ({ target: `${calendarId}/${eventId}`, revision: etag, payload: view, preview: view })
|
|
37000
|
+
}
|
|
37001
|
+
});
|
|
37002
|
+
if (confirmation) return confirmation;
|
|
37003
|
+
}
|
|
36751
37004
|
return runOrDiagnose(["calendar", "delete", pos(calendarId), pos(eventId), "--force"], { account });
|
|
36752
37005
|
});
|
|
36753
37006
|
server.registerTool("gog_calendar_respond", {
|
|
@@ -36869,8 +37122,14 @@ function inlineAttachmentArgs(flag, attachments, siblingArgs = []) {
|
|
|
36869
37122
|
|
|
36870
37123
|
// src/tools/chat.ts
|
|
36871
37124
|
var CHAT_SEND_WORDS = /* @__PURE__ */ new Set(["send", "create", "post"]);
|
|
37125
|
+
var CHAT_SPACE_CREATE_WORDS = /* @__PURE__ */ new Set(["create", "add", "new"]);
|
|
36872
37126
|
function vetChatRun(subcommand, args) {
|
|
36873
37127
|
const sub = subcommand.toLowerCase();
|
|
37128
|
+
if (sub === "spaces") {
|
|
37129
|
+
const word2 = hasCommandWord(args, CHAT_SPACE_CREATE_WORDS);
|
|
37130
|
+
const withMembers = args.some((a) => /^--members?(?:=|$)/i.test(a));
|
|
37131
|
+
return word2 && withMembers ? gatedElsewhere(`gog chat spaces ${word2.toLowerCase()} --member`, "gog_chat_run", "adds people to a space", "gog_chat_spaces_create") : void 0;
|
|
37132
|
+
}
|
|
36874
37133
|
if (sub !== "messages" && sub !== "dm") return void 0;
|
|
36875
37134
|
const word = hasCommandWord(args, CHAT_SEND_WORDS);
|
|
36876
37135
|
if (!word) return void 0;
|
|
@@ -36921,16 +37180,34 @@ function registerChatTools(server) {
|
|
|
36921
37180
|
return runOrDiagnose(args, { account });
|
|
36922
37181
|
});
|
|
36923
37182
|
server.registerTool("gog_chat_spaces_create", {
|
|
36924
|
-
description: "Create a named Chat space, optionally seeding its membership. Members are added immediately and are notified
|
|
37183
|
+
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
37184
|
annotations: { destructiveHint: true },
|
|
36926
37185
|
inputSchema: external_exports.object({
|
|
36927
37186
|
displayName: external_exports.string().describe("Display name for the new space"),
|
|
36928
37187
|
members: external_exports.array(external_exports.string()).optional().describe('Initial members, as email addresses or "users/..." resource names'),
|
|
36929
|
-
account: accountParam
|
|
37188
|
+
account: accountParam,
|
|
37189
|
+
confirmToken: confirmTokenParam
|
|
36930
37190
|
})
|
|
36931
|
-
}, async ({ displayName, members: members2, account }) => {
|
|
37191
|
+
}, async ({ displayName, members: members2, account, confirmToken }, ctx) => {
|
|
36932
37192
|
const args = ["chat", "spaces", "create", pos(displayName)];
|
|
36933
37193
|
if (members2) for (const member of members2) args.push(`--member=${member}`);
|
|
37194
|
+
if (members2?.length) {
|
|
37195
|
+
const space = { displayName, members: members2 };
|
|
37196
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
37197
|
+
action: "chat.space-create",
|
|
37198
|
+
message: "Review and confirm this Chat space \u2014 every member is added and notified:",
|
|
37199
|
+
confirmationLabel: "Confirm that these people should be added to a new space now.",
|
|
37200
|
+
details: space,
|
|
37201
|
+
unsupportedNote: "Create the space without members instead; the user can add people from Google Chat.",
|
|
37202
|
+
fallback: {
|
|
37203
|
+
tool: "gog_chat_spaces_create",
|
|
37204
|
+
account,
|
|
37205
|
+
confirmToken,
|
|
37206
|
+
subject: () => ({ target: displayName, payload: space, preview: space })
|
|
37207
|
+
}
|
|
37208
|
+
});
|
|
37209
|
+
if (confirmation) return confirmation;
|
|
37210
|
+
}
|
|
36934
37211
|
return runOrDiagnose(args, { account });
|
|
36935
37212
|
});
|
|
36936
37213
|
server.registerTool("gog_chat_threads_list", {
|
|
@@ -37124,20 +37401,85 @@ function registerChatTools(server) {
|
|
|
37124
37401
|
|
|
37125
37402
|
// src/tools/classroom.ts
|
|
37126
37403
|
var CLASSROOM_CREATE_WORDS = /* @__PURE__ */ new Set(["create", "add", "new"]);
|
|
37127
|
-
var
|
|
37128
|
-
|
|
37129
|
-
|
|
37130
|
-
|
|
37131
|
-
|
|
37132
|
-
|
|
37133
|
-
|
|
37404
|
+
var CLASSROOM_DELETE_WORDS = /* @__PURE__ */ new Set(["delete", "rm", "del", "remove"]);
|
|
37405
|
+
var CLASSROOM_UPDATE_WORDS = /* @__PURE__ */ new Set(["update", "edit", "set"]);
|
|
37406
|
+
var CLASSROOM_RETURN_WORDS = /* @__PURE__ */ new Set(["return", "send"]);
|
|
37407
|
+
var CLASSROOM_ASSIGNEE_WORDS = /* @__PURE__ */ new Set(["assignees", "assign"]);
|
|
37408
|
+
var CLASSROOM_GROUPS = {
|
|
37409
|
+
courses: "courses",
|
|
37410
|
+
course: "courses",
|
|
37411
|
+
announcements: "announcements",
|
|
37412
|
+
announcement: "announcements",
|
|
37413
|
+
ann: "announcements",
|
|
37414
|
+
invitations: "invitations",
|
|
37415
|
+
invitation: "invitations",
|
|
37416
|
+
invites: "invitations",
|
|
37417
|
+
coursework: "coursework",
|
|
37418
|
+
work: "coursework",
|
|
37419
|
+
materials: "materials",
|
|
37420
|
+
material: "materials",
|
|
37421
|
+
students: "students",
|
|
37422
|
+
student: "students",
|
|
37423
|
+
teachers: "teachers",
|
|
37424
|
+
teacher: "teachers",
|
|
37425
|
+
submissions: "submissions",
|
|
37426
|
+
submission: "submissions",
|
|
37427
|
+
"guardian-invitations": "guardian-invitations",
|
|
37428
|
+
"guardian-invites": "guardian-invitations"
|
|
37134
37429
|
};
|
|
37430
|
+
function publishesToStudents(args) {
|
|
37431
|
+
return flagValue(args, "state")?.toUpperCase() === "PUBLISHED" || flagValue(args, "scheduled") !== void 0;
|
|
37432
|
+
}
|
|
37433
|
+
function createsVisibleWork(args) {
|
|
37434
|
+
return flagValue(args, "state")?.toUpperCase() !== "DRAFT" || flagValue(args, "scheduled") !== void 0;
|
|
37435
|
+
}
|
|
37436
|
+
function widensAssignees(args) {
|
|
37437
|
+
return flagValue(args, "mode")?.toUpperCase() === "ALL_STUDENTS" || flagValue(args, "add-student") !== void 0;
|
|
37438
|
+
}
|
|
37135
37439
|
function vetClassroomRun(subcommand, args) {
|
|
37136
37440
|
const sub = subcommand.toLowerCase();
|
|
37137
|
-
|
|
37138
|
-
|
|
37139
|
-
const
|
|
37140
|
-
|
|
37441
|
+
if (!Object.hasOwn(CLASSROOM_GROUPS, sub)) return void 0;
|
|
37442
|
+
const group = CLASSROOM_GROUPS[sub];
|
|
37443
|
+
const via = "gog_classroom_run";
|
|
37444
|
+
const what = (word) => `gog classroom ${sub} ${word.toLowerCase()}`;
|
|
37445
|
+
const create = hasCommandWord(args, CLASSROOM_CREATE_WORDS);
|
|
37446
|
+
const update = hasCommandWord(args, CLASSROOM_UPDATE_WORDS);
|
|
37447
|
+
const remove = hasCommandWord(args, CLASSROOM_DELETE_WORDS);
|
|
37448
|
+
const widenedAssignees = () => {
|
|
37449
|
+
const word = hasCommandWord(args, CLASSROOM_ASSIGNEE_WORDS);
|
|
37450
|
+
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;
|
|
37451
|
+
};
|
|
37452
|
+
switch (group) {
|
|
37453
|
+
case "courses":
|
|
37454
|
+
return remove ? gatedElsewhere(what(remove), via, "deletes a course", "gog_classroom_courses_delete") : void 0;
|
|
37455
|
+
case "announcements":
|
|
37456
|
+
if (create) return gatedElsewhere(what(create), via, "posts to a class", "gog_classroom_announcements_create");
|
|
37457
|
+
if (update && publishesToStudents(args)) return gatedElsewhere(what(update), via, "publishes an announcement to a class", "gog_classroom_announcements_update");
|
|
37458
|
+
return widenedAssignees();
|
|
37459
|
+
case "invitations":
|
|
37460
|
+
return create ? gatedElsewhere(what(create), via, "invites someone to a class", "gog_classroom_invitations_create") : void 0;
|
|
37461
|
+
case "coursework":
|
|
37462
|
+
if (create && createsVisibleWork(args)) return gatedElsewhere(what(create), via, "posts coursework to a class", "gog_classroom_coursework_create");
|
|
37463
|
+
if (remove) return gatedElsewhere(what(remove), via, "deletes coursework and every submission to it", "gog_classroom_coursework_delete");
|
|
37464
|
+
if (update && publishesToStudents(args)) return gatedElsewhere(what(update), via, "publishes work to students", "gog_classroom_coursework_update");
|
|
37465
|
+
return widenedAssignees();
|
|
37466
|
+
case "materials":
|
|
37467
|
+
if (create && createsVisibleWork(args)) {
|
|
37468
|
+
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.");
|
|
37469
|
+
}
|
|
37470
|
+
if (update && publishesToStudents(args)) return refusedInRun(what(update), via, "publishes material to students", "Ask the user to publish it from Classroom.");
|
|
37471
|
+
return void 0;
|
|
37472
|
+
case "students":
|
|
37473
|
+
return create ? gatedElsewhere(what(create), via, "enrols someone in a class", "gog_classroom_students_add") : void 0;
|
|
37474
|
+
case "teachers":
|
|
37475
|
+
return create ? gatedElsewhere(what(create), via, "gives someone a teacher's access to a class and its roster", "gog_classroom_teachers_add") : void 0;
|
|
37476
|
+
case "submissions": {
|
|
37477
|
+
const word = hasCommandWord(args, CLASSROOM_RETURN_WORDS);
|
|
37478
|
+
return word ? gatedElsewhere(what(word), via, "returns work to a student, who is notified", "gog_classroom_submissions_return") : void 0;
|
|
37479
|
+
}
|
|
37480
|
+
case "guardian-invitations":
|
|
37481
|
+
return create ? refusedInRun(what(create), via, "emails a guardian invitation", "Ask the user to invite the guardian from Classroom.") : void 0;
|
|
37482
|
+
}
|
|
37141
37483
|
}
|
|
37142
37484
|
async function readCourse(courseId, account, runner = runOrDiagnose) {
|
|
37143
37485
|
const got = await runner(["classroom", "courses", "get", pos(courseId)], { account });
|
|
@@ -37156,6 +37498,52 @@ async function readCourse(courseId, account, runner = runOrDiagnose) {
|
|
|
37156
37498
|
}
|
|
37157
37499
|
};
|
|
37158
37500
|
}
|
|
37501
|
+
function nested(raw, key) {
|
|
37502
|
+
try {
|
|
37503
|
+
const parsed = JSON.parse(raw);
|
|
37504
|
+
const inner = parsed?.[key];
|
|
37505
|
+
return inner && typeof inner === "object" ? inner : void 0;
|
|
37506
|
+
} catch {
|
|
37507
|
+
return void 0;
|
|
37508
|
+
}
|
|
37509
|
+
}
|
|
37510
|
+
var str = (v) => typeof v === "string" ? v : void 0;
|
|
37511
|
+
var num = (v) => typeof v === "number" ? v : void 0;
|
|
37512
|
+
async function readCoursework(courseId, courseworkId, account, runner = runOrDiagnose) {
|
|
37513
|
+
const got = await runner(["classroom", "coursework", "get", pos(courseId), pos(courseworkId)], { account });
|
|
37514
|
+
if (got.isError) return { error: got };
|
|
37515
|
+
const work = nested(resultText(got), "coursework");
|
|
37516
|
+
const title = str(work?.title);
|
|
37517
|
+
return { coursework: { id: courseworkId, ...title !== void 0 ? { title } : {} } };
|
|
37518
|
+
}
|
|
37519
|
+
async function readSubmission(courseId, courseworkId, submissionId, account, runner = runOrDiagnose) {
|
|
37520
|
+
const got = await runner(["classroom", "submissions", "get", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
37521
|
+
if (got.isError) return { error: got };
|
|
37522
|
+
const sub = nested(resultText(got), "submission");
|
|
37523
|
+
const fields = {
|
|
37524
|
+
student: str(sub?.userId),
|
|
37525
|
+
state: str(sub?.state),
|
|
37526
|
+
draftGrade: num(sub?.draftGrade),
|
|
37527
|
+
assignedGrade: num(sub?.assignedGrade)
|
|
37528
|
+
};
|
|
37529
|
+
const updateTime = str(sub?.updateTime);
|
|
37530
|
+
return {
|
|
37531
|
+
submission: {
|
|
37532
|
+
id: submissionId,
|
|
37533
|
+
...Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== void 0))
|
|
37534
|
+
},
|
|
37535
|
+
...updateTime !== void 0 ? { updateTime } : {}
|
|
37536
|
+
};
|
|
37537
|
+
}
|
|
37538
|
+
async function studentLabel(courseId, userId, account, runner = runOrDiagnose) {
|
|
37539
|
+
const got = await runner(["classroom", "students", "get", pos(courseId), pos(userId)], { account });
|
|
37540
|
+
if (got.isError) return userId;
|
|
37541
|
+
const profile = nested(resultText(got), "student")?.profile;
|
|
37542
|
+
const name = str(profile?.name?.fullName);
|
|
37543
|
+
const email3 = str(profile?.emailAddress);
|
|
37544
|
+
if (name && email3) return `${name} <${email3}>`;
|
|
37545
|
+
return name ?? email3 ?? userId;
|
|
37546
|
+
}
|
|
37159
37547
|
function registerClassroomTools(server) {
|
|
37160
37548
|
server.registerTool("gog_classroom_courses_list", {
|
|
37161
37549
|
description: "List Google Classroom courses.",
|
|
@@ -37368,15 +37756,38 @@ function registerClassroomTools(server) {
|
|
|
37368
37756
|
return runOrDiagnose(args, { account });
|
|
37369
37757
|
});
|
|
37370
37758
|
server.registerTool("gog_classroom_submissions_return", {
|
|
37371
|
-
description: "Return a graded submission to the student.",
|
|
37759
|
+
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
37760
|
annotations: { destructiveHint: true },
|
|
37373
37761
|
inputSchema: external_exports.object({
|
|
37374
37762
|
courseId: external_exports.string().describe("Course ID"),
|
|
37375
37763
|
courseworkId: external_exports.string().describe("Coursework ID"),
|
|
37376
37764
|
submissionId: external_exports.string().describe("Submission ID"),
|
|
37377
|
-
account: accountParam
|
|
37765
|
+
account: accountParam,
|
|
37766
|
+
confirmToken: confirmTokenParam
|
|
37378
37767
|
})
|
|
37379
|
-
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
37768
|
+
}, async ({ courseId, courseworkId, submissionId, account, confirmToken }, ctx) => {
|
|
37769
|
+
const course = await readCourse(courseId, account);
|
|
37770
|
+
if (course.error) return course.error;
|
|
37771
|
+
const work = await readCoursework(courseId, courseworkId, account);
|
|
37772
|
+
if (work.error) return work.error;
|
|
37773
|
+
const sub = await readSubmission(courseId, courseworkId, submissionId, account);
|
|
37774
|
+
if (sub.error) return sub.error;
|
|
37775
|
+
const submission = sub.submission.student ? { ...sub.submission, student: await studentLabel(courseId, sub.submission.student, account) } : sub.submission;
|
|
37776
|
+
const view = { course: course.course, coursework: work.coursework, submission };
|
|
37777
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
37778
|
+
action: "classroom.submission-return",
|
|
37779
|
+
message: "Review and confirm returning this work \u2014 the student is notified and sees the grade:",
|
|
37780
|
+
confirmationLabel: "Confirm that this submission should be returned to the student now.",
|
|
37781
|
+
details: view,
|
|
37782
|
+
unsupportedNote: "Ask the user to return it from Classroom.",
|
|
37783
|
+
fallback: {
|
|
37784
|
+
tool: "gog_classroom_submissions_return",
|
|
37785
|
+
account,
|
|
37786
|
+
confirmToken,
|
|
37787
|
+
subject: () => ({ target: `${courseId}/${courseworkId}/${submissionId}`, revision: sub.updateTime, payload: view, preview: view })
|
|
37788
|
+
}
|
|
37789
|
+
});
|
|
37790
|
+
if (confirmation) return confirmation;
|
|
37380
37791
|
return runOrDiagnose(["classroom", "submissions", "return", pos(courseId), pos(courseworkId), pos(submissionId)], { account });
|
|
37381
37792
|
});
|
|
37382
37793
|
server.registerTool("gog_classroom_submissions_turn_in", {
|
|
@@ -37438,13 +37849,13 @@ function registerClassroomTools(server) {
|
|
|
37438
37849
|
return runOrDiagnose(["classroom", "announcements", "get", pos(courseId), pos(announcementId)], { account });
|
|
37439
37850
|
});
|
|
37440
37851
|
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,
|
|
37852
|
+
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
37853
|
annotations: { destructiveHint: true },
|
|
37443
37854
|
inputSchema: external_exports.object({
|
|
37444
37855
|
courseId: external_exports.string().describe("Course ID"),
|
|
37445
37856
|
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"),
|
|
37857
|
+
state: external_exports.enum(["PUBLISHED", "DRAFT"]).optional().describe("State (an unscheduled DRAFT is visible only to teachers and needs no confirmation)"),
|
|
37858
|
+
scheduled: external_exports.string().optional().describe("Scheduled publish time (asks the user to confirm, even for a DRAFT)"),
|
|
37448
37859
|
account: accountParam,
|
|
37449
37860
|
confirmToken: confirmTokenParam
|
|
37450
37861
|
})
|
|
@@ -37452,7 +37863,7 @@ function registerClassroomTools(server) {
|
|
|
37452
37863
|
const args = ["classroom", "announcements", "create", pos(courseId), `--text=${text}`];
|
|
37453
37864
|
if (state) args.push(`--state=${state}`);
|
|
37454
37865
|
if (scheduled) args.push(`--scheduled=${scheduled}`);
|
|
37455
|
-
if (state !== "DRAFT") {
|
|
37866
|
+
if (state !== "DRAFT" || scheduled) {
|
|
37456
37867
|
const read = await readCourse(courseId, account);
|
|
37457
37868
|
if (read.error) return read.error;
|
|
37458
37869
|
const publishes = scheduled ? `at ${scheduled}` : "immediately";
|
|
@@ -37624,6 +38035,9 @@ function registerContactsTools(server) {
|
|
|
37624
38035
|
}
|
|
37625
38036
|
|
|
37626
38037
|
// src/tools/docs.ts
|
|
38038
|
+
function vetDocsRun(subcommand, args) {
|
|
38039
|
+
return vetCommentsRun("docs", subcommand, args);
|
|
38040
|
+
}
|
|
37627
38041
|
function registerDocsTools(server) {
|
|
37628
38042
|
server.registerTool("gog_docs_info", {
|
|
37629
38043
|
description: "Get Google Doc metadata: title, ID, and other properties.",
|
|
@@ -37720,12 +38134,29 @@ function registerDocsTools(server) {
|
|
|
37720
38134
|
}, async ({ docId, account }) => {
|
|
37721
38135
|
return runOrDiagnose(["docs", "structure", pos(docId)], { account });
|
|
37722
38136
|
});
|
|
37723
|
-
registerRunTool(server, { service: "docs", examples: '"copy", "clear", "insert", "sed", "export"' });
|
|
38137
|
+
registerRunTool(server, { service: "docs", examples: '"copy", "clear", "insert", "sed", "export"', vet: vetDocsRun });
|
|
37724
38138
|
}
|
|
37725
38139
|
|
|
37726
38140
|
// src/tools/drive.ts
|
|
37727
|
-
|
|
37728
|
-
|
|
38141
|
+
var DRIVE_DELETE_WORDS = /* @__PURE__ */ new Set(["delete", "rm", "del"]);
|
|
38142
|
+
function vetDriveRun(subcommand, args) {
|
|
38143
|
+
const sub = subcommand.toLowerCase();
|
|
38144
|
+
if (sub === "share") return gatedElsewhere("gog drive share", "gog_drive_run", "grants access to a file", "gog_drive_share");
|
|
38145
|
+
if (DRIVE_DELETE_WORDS.has(sub) && hasTrueFlag(args, "permanent")) {
|
|
38146
|
+
return gatedElsewhere(`gog drive ${sub} --permanent`, "gog_drive_run", "deletes a file for good, bypassing the trash", "gog_drive_delete");
|
|
38147
|
+
}
|
|
38148
|
+
if (sub === "bulk") {
|
|
38149
|
+
return gatedElsewhere(
|
|
38150
|
+
"gog drive bulk",
|
|
38151
|
+
"gog_drive_run",
|
|
38152
|
+
"rewrites sharing permissions across every matching file in a tree",
|
|
38153
|
+
"gog_drive_share (one file at a time)"
|
|
38154
|
+
);
|
|
38155
|
+
}
|
|
38156
|
+
if (sub === "unshare") {
|
|
38157
|
+
return refusedInRun("gog drive unshare", "gog_drive_run", "removes someone's access to a file", "Use gog_drive_unshare.");
|
|
38158
|
+
}
|
|
38159
|
+
return vetCommentsRun("drive", sub, args);
|
|
37729
38160
|
}
|
|
37730
38161
|
var GOOGLE_DOC_MIME = "application/vnd.google-apps.document";
|
|
37731
38162
|
function shareTargetMeta(raw) {
|
|
@@ -37844,16 +38275,36 @@ function registerDriveTools(server) {
|
|
|
37844
38275
|
return runOrDiagnose(["drive", "move", pos(fileId), `--parent=${parentId}`], { account });
|
|
37845
38276
|
});
|
|
37846
38277
|
server.registerTool("gog_drive_delete", {
|
|
37847
|
-
description: "Move a Google Drive file to trash, or permanently delete it with permanent=true (irreversible).",
|
|
38278
|
+
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
38279
|
annotations: { destructiveHint: true },
|
|
37849
38280
|
inputSchema: external_exports.object({
|
|
37850
38281
|
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
|
|
38282
|
+
permanent: external_exports.boolean().optional().describe("Permanently delete instead of moving to trash (irreversible; asks the user first)"),
|
|
38283
|
+
account: accountParam,
|
|
38284
|
+
confirmToken: confirmTokenParam
|
|
37853
38285
|
})
|
|
37854
|
-
}, async ({ fileId, permanent, account }) => {
|
|
38286
|
+
}, async ({ fileId, permanent, account, confirmToken }, ctx) => {
|
|
37855
38287
|
const args = ["drive", "delete", pos(fileId)];
|
|
37856
|
-
if (permanent)
|
|
38288
|
+
if (permanent) {
|
|
38289
|
+
const got = await runOrDiagnose(["drive", "get", pos(fileId)], { account });
|
|
38290
|
+
if (got.isError) return got;
|
|
38291
|
+
const target = { file: { id: fileId, ...shareTargetMeta(resultText(got)) }, permanent: true };
|
|
38292
|
+
const confirmation = await requireDispatchConfirmation(ctx, {
|
|
38293
|
+
action: "drive.delete-permanent",
|
|
38294
|
+
message: "Review and confirm PERMANENTLY deleting this file \u2014 it skips the trash and cannot be recovered:",
|
|
38295
|
+
confirmationLabel: "Confirm that this file should be permanently deleted now.",
|
|
38296
|
+
details: target,
|
|
38297
|
+
unsupportedNote: "Move it to the trash instead (permanent=false); the user can empty the trash from Google Drive.",
|
|
38298
|
+
fallback: {
|
|
38299
|
+
tool: "gog_drive_delete",
|
|
38300
|
+
account,
|
|
38301
|
+
confirmToken,
|
|
38302
|
+
subject: () => ({ target: fileId, payload: target, preview: target })
|
|
38303
|
+
}
|
|
38304
|
+
});
|
|
38305
|
+
if (confirmation) return confirmation;
|
|
38306
|
+
args.push("--permanent");
|
|
38307
|
+
}
|
|
37857
38308
|
args.push("--force");
|
|
37858
38309
|
return runOrDiagnose(args, { account });
|
|
37859
38310
|
});
|
|
@@ -38179,18 +38630,22 @@ function trustedDomains(account) {
|
|
|
38179
38630
|
}
|
|
38180
38631
|
function logGmailDispatch(tool, recipients, account) {
|
|
38181
38632
|
const domains = trustedDomains(account);
|
|
38182
|
-
const
|
|
38633
|
+
const externalDomains = /* @__PURE__ */ new Set();
|
|
38634
|
+
let externalRecipientCount = 0;
|
|
38635
|
+
for (const recipient of recipients) {
|
|
38183
38636
|
const at = recipient.indexOf("@");
|
|
38184
|
-
const domain2 = at > -1 ? recipient.slice(at + 1) : "";
|
|
38185
|
-
|
|
38186
|
-
|
|
38637
|
+
const domain2 = at > -1 ? recipient.slice(at + 1).toLowerCase() : "";
|
|
38638
|
+
if (domain2 && domains.has(domain2)) continue;
|
|
38639
|
+
externalRecipientCount += 1;
|
|
38640
|
+
externalDomains.add(domain2 || "(no domain)");
|
|
38641
|
+
}
|
|
38187
38642
|
const event = {
|
|
38188
38643
|
event: "gmail_dispatch",
|
|
38189
38644
|
tool,
|
|
38190
38645
|
recipientCount: recipients.length,
|
|
38191
|
-
externalRecipientCount
|
|
38192
|
-
hasExternalRecipients:
|
|
38193
|
-
|
|
38646
|
+
externalRecipientCount,
|
|
38647
|
+
hasExternalRecipients: externalRecipientCount > 0,
|
|
38648
|
+
externalDomains: [...externalDomains].sort(),
|
|
38194
38649
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
38195
38650
|
};
|
|
38196
38651
|
process.stderr.write(`${JSON.stringify(event)}
|
|
@@ -38357,6 +38812,39 @@ async function sendReply(kind, toolName, messageId, account, flags, ctx, confirm
|
|
|
38357
38812
|
return result;
|
|
38358
38813
|
}
|
|
38359
38814
|
var GMAIL_RUN_BLOCKED_SETTINGS = /* @__PURE__ */ new Set(["forwarding", "autoforward", "filters", "delegates"]);
|
|
38815
|
+
var GMAIL_DELETE_WORDS = /* @__PURE__ */ new Set(["delete", "rm", "del", "remove"]);
|
|
38816
|
+
var GMAIL_CREATE_WORDS = /* @__PURE__ */ new Set(["create", "add", "new"]);
|
|
38817
|
+
var GMAIL_UPDATE_WORDS = /* @__PURE__ */ new Set(["update", "edit", "set"]);
|
|
38818
|
+
var SENDAS = /* @__PURE__ */ new Set(["sendas"]);
|
|
38819
|
+
var VACATION = /* @__PURE__ */ new Set(["vacation"]);
|
|
38820
|
+
function vetGmailGatedRun(sub, args) {
|
|
38821
|
+
if (sub === "batch") {
|
|
38822
|
+
const word = hasCommandWord(args, GMAIL_DELETE_WORDS);
|
|
38823
|
+
return word ? gatedElsewhere(`gog gmail batch ${word.toLowerCase()}`, "gog_gmail_run", "deletes mail for good, bypassing the Trash", "gog_gmail_batch_delete") : void 0;
|
|
38824
|
+
}
|
|
38825
|
+
if (sub !== "settings" && sub !== "sendas" && sub !== "vacation") return void 0;
|
|
38826
|
+
const words = sub === "settings" ? args : [sub, ...args];
|
|
38827
|
+
if (hasCommandWord(words, SENDAS)) {
|
|
38828
|
+
const word = hasCommandWord(args, GMAIL_CREATE_WORDS);
|
|
38829
|
+
if (word) {
|
|
38830
|
+
return gatedElsewhere(
|
|
38831
|
+
`gog gmail sendas ${word.toLowerCase()}`,
|
|
38832
|
+
"gog_gmail_run",
|
|
38833
|
+
"makes Google email the address and adds a sending identity",
|
|
38834
|
+
"gog_gmail_sendas_create"
|
|
38835
|
+
);
|
|
38836
|
+
}
|
|
38837
|
+
}
|
|
38838
|
+
if (hasCommandWord(words, VACATION) && hasCommandWord(args, GMAIL_UPDATE_WORDS) && hasTrueFlag(args, "enable")) {
|
|
38839
|
+
return gatedElsewhere(
|
|
38840
|
+
"gog gmail vacation update --enable",
|
|
38841
|
+
"gog_gmail_run",
|
|
38842
|
+
"turns on an auto-reply to every sender",
|
|
38843
|
+
"gog_gmail_vacation_update"
|
|
38844
|
+
);
|
|
38845
|
+
}
|
|
38846
|
+
return void 0;
|
|
38847
|
+
}
|
|
38360
38848
|
function vetGmailRun(subcommand, args) {
|
|
38361
38849
|
if (subcommand === "autoreply") {
|
|
38362
38850
|
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 +38852,8 @@ function vetGmailRun(subcommand, args) {
|
|
|
38364
38852
|
if (GMAIL_RUN_BLOCKED_SETTINGS.has(subcommand)) {
|
|
38365
38853
|
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
38854
|
}
|
|
38855
|
+
const gated = vetGmailGatedRun(subcommand.toLowerCase(), args);
|
|
38856
|
+
if (gated) return gated;
|
|
38367
38857
|
if (subcommand === "settings") {
|
|
38368
38858
|
const blocked = hasCommandWord(args, GMAIL_RUN_BLOCKED_SETTINGS);
|
|
38369
38859
|
if (blocked) {
|
|
@@ -38575,6 +39065,13 @@ function countNonEmptyCells(getOutput) {
|
|
|
38575
39065
|
}
|
|
38576
39066
|
|
|
38577
39067
|
// src/tools/sheets.ts
|
|
39068
|
+
var DATASOURCE_ALIASES = /* @__PURE__ */ new Set(["datasource", "data-source", "data-sources", "connected-sheets"]);
|
|
39069
|
+
var DATASOURCE_BILLED = /* @__PURE__ */ new Set(["add", "update", "refresh"]);
|
|
39070
|
+
function vetSheetsRun(subcommand, args) {
|
|
39071
|
+
if (!DATASOURCE_ALIASES.has(subcommand.toLowerCase())) return void 0;
|
|
39072
|
+
const word = hasCommandWord(args, DATASOURCE_BILLED)?.toLowerCase();
|
|
39073
|
+
return word ? gatedElsewhere(`gog sheets datasource ${word}`, "gog_sheets_run", "runs a billed BigQuery job", `gog_sheets_datasource_${word}`) : void 0;
|
|
39074
|
+
}
|
|
38578
39075
|
var cellValueParam = external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean(), external_exports.null()]);
|
|
38579
39076
|
var dryRunParam = external_exports.boolean().optional().describe(
|
|
38580
39077
|
"Preview the operation without modifying the sheet (gog --dry-run): reports the intended actions and exits without writing."
|
|
@@ -38690,7 +39187,7 @@ function registerSheetsTools(server) {
|
|
|
38690
39187
|
}, async ({ spreadsheetId, find, replace, account }) => {
|
|
38691
39188
|
return runOrDiagnose(["sheets", "find-replace", pos(spreadsheetId), pos(find), pos(replace)], { account });
|
|
38692
39189
|
});
|
|
38693
|
-
registerRunTool(server, { service: "sheets", examples: '"freeze", "add-tab", "rename-tab"' });
|
|
39190
|
+
registerRunTool(server, { service: "sheets", examples: '"freeze", "add-tab", "rename-tab"', vet: vetSheetsRun });
|
|
38694
39191
|
}
|
|
38695
39192
|
|
|
38696
39193
|
// src/tools/slides.ts
|
|
@@ -38854,7 +39351,7 @@ function registerTasksTools(server) {
|
|
|
38854
39351
|
}
|
|
38855
39352
|
|
|
38856
39353
|
// src/server.ts
|
|
38857
|
-
var VERSION = true ? "4.5.
|
|
39354
|
+
var VERSION = true ? "4.5.2" : "0.0.0";
|
|
38858
39355
|
var BASE_TOOL_REGISTRARS = [
|
|
38859
39356
|
registerApiTools,
|
|
38860
39357
|
registerAppScriptTools,
|
|
@@ -38944,7 +39441,7 @@ await runMcp({
|
|
|
38944
39441
|
});
|
|
38945
39442
|
/*! Bundled license information:
|
|
38946
39443
|
|
|
38947
|
-
@modelcontextprotocol/server/dist/src-
|
|
39444
|
+
@modelcontextprotocol/server/dist/src-D-y6h4N7.mjs:
|
|
38948
39445
|
(*!
|
|
38949
39446
|
* content-type
|
|
38950
39447
|
* Copyright(c) 2015 Douglas Christopher Wilson
|