gogcli-mcp-drive 4.0.1 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "Extended Google Drive for Claude via gogcli — auth + full Drive support (upload, download, permissions, comments, shared drives)",
10
- "version": "4.0.1"
10
+ "version": "4.2.0"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -15,7 +15,7 @@
15
15
  "displayName": "gogcli (Drive)",
16
16
  "source": "./",
17
17
  "description": "Extended Google Drive for Claude via gogcli — auth + full Drive support (upload, download, permissions, comments, shared drives)",
18
- "version": "4.0.1",
18
+ "version": "4.2.0",
19
19
  "author": {
20
20
  "name": "Chris Hall"
21
21
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gogcli-mcp-drive",
3
3
  "displayName": "gogcli (Drive)",
4
- "version": "4.0.1",
4
+ "version": "4.2.0",
5
5
  "description": "Extended Google Drive for Claude via gogcli — auth + full Drive support (upload, download, permissions, comments, shared drives)",
6
6
  "author": {
7
7
  "name": "Chris Hall",
package/README.md CHANGED
@@ -8,7 +8,7 @@ Extended Google Drive [MCP](https://modelcontextprotocol.io) server via [gogcli]
8
8
  ## Requirements
9
9
 
10
10
  - [gogcli](https://github.com/openclaw/gogcli) installed and authenticated
11
- - Node.js 18+
11
+ - Node.js 22+
12
12
 
13
13
  ```bash
14
14
  brew install gogcli
package/SKILL.md CHANGED
@@ -12,7 +12,7 @@ Extended Google Drive MCP server via [gogcli](https://github.com/openclaw/gogcli
12
12
  ## Requirements
13
13
 
14
14
  - [gogcli](https://github.com/openclaw/gogcli) installed and authenticated
15
- - Node.js 18 or later
15
+ - Node.js 22 or later
16
16
 
17
17
  ## Setup
18
18
 
package/dist/index.js CHANGED
@@ -5,6 +5,32 @@ var __export = (target, all) => {
5
5
  __defProp(target, name, { get: all[name], enumerable: true });
6
6
  };
7
7
 
8
+ // ../../node_modules/@chrischall/mcp-utils/dist/cancel/index.js
9
+ import { AsyncLocalStorage } from "node:async_hooks";
10
+ var storage = new AsyncLocalStorage();
11
+ function withCallSignal(signal, fn) {
12
+ return signal ? storage.run({ signal }, fn) : fn();
13
+ }
14
+ function currentCallSignal() {
15
+ return storage.getStore()?.signal;
16
+ }
17
+ function killOnCancel(child, signal = "SIGTERM") {
18
+ const ambient = currentCallSignal();
19
+ if (!ambient)
20
+ return () => {
21
+ };
22
+ if (ambient.aborted) {
23
+ child.kill(signal);
24
+ return () => {
25
+ };
26
+ }
27
+ const onAbort = () => {
28
+ child.kill(signal);
29
+ };
30
+ ambient.addEventListener("abort", onAbort, { once: true });
31
+ return () => ambient.removeEventListener("abort", onAbort);
32
+ }
33
+
8
34
  // ../../node_modules/@modelcontextprotocol/server/dist/chunk-Br0eD_fh.mjs
9
35
  var __create = Object.create;
10
36
  var __defProp2 = Object.defineProperty;
@@ -20845,6 +20871,7 @@ function missingClientCapabilities(required2, declared) {
20845
20871
  return Object.keys(missing).length > 0 ? missing : void 0;
20846
20872
  }
20847
20873
  var FIRST_MODERN_PROTOCOL_VERSION = "2026-07-28";
20874
+ var SUPPORTED_MODERN_PROTOCOL_VERSIONS = [FIRST_MODERN_PROTOCOL_VERSION];
20848
20875
  function isModernProtocolVersion(version2) {
20849
20876
  return version2 >= FIRST_MODERN_PROTOCOL_VERSION;
20850
20877
  }
@@ -23403,6 +23430,25 @@ function isSpecNotificationMethod(method) {
23403
23430
  return ALL_CODECS.some((codec2) => codec2.hasNotificationMethod(method));
23404
23431
  }
23405
23432
  var ALL_CODECS = [rev2025Codec, rev2026Codec];
23433
+ function isPlainObject$3(value) {
23434
+ return value !== null && typeof value === "object" && !Array.isArray(value);
23435
+ }
23436
+ function requestMetaOf(params) {
23437
+ if (!isPlainObject$3(params)) return void 0;
23438
+ const meta3 = params["_meta"];
23439
+ return isPlainObject$3(meta3) ? meta3 : void 0;
23440
+ }
23441
+ function hasEnvelopeClaim(params) {
23442
+ const meta3 = requestMetaOf(params);
23443
+ return meta3 !== void 0 && PROTOCOL_VERSION_META_KEY in meta3;
23444
+ }
23445
+ function envelopeClaimVersion(params) {
23446
+ const value = requestMetaOf(params)?.[PROTOCOL_VERSION_META_KEY];
23447
+ return typeof value === "string" ? value : void 0;
23448
+ }
23449
+ function validateEnvelopeMeta(meta3) {
23450
+ return codecForVersion(MODERN_WIRE_REVISION).validateEnvelopeMeta(meta3);
23451
+ }
23406
23452
  var schemas_exports3 = /* @__PURE__ */ __exportAll({
23407
23453
  AnnotationsSchema: () => AnnotationsSchema,
23408
23454
  AudioContentSchema: () => AudioContentSchema,
@@ -23753,6 +23799,45 @@ var LADDER_ERROR_HTTP_STATUS = {
23753
23799
  [ProtocolErrorCode.MissingRequiredClientCapability]: 400,
23754
23800
  [HEADER_MISMATCH_ERROR_CODE]: 400
23755
23801
  };
23802
+ function rejection(rung, cell, httpStatus, error62, settled) {
23803
+ return {
23804
+ kind: "reject",
23805
+ rung,
23806
+ cell,
23807
+ httpStatus,
23808
+ code: error62.code,
23809
+ message: error62.message,
23810
+ ...error62.data !== void 0 && { data: error62.data },
23811
+ settled
23812
+ };
23813
+ }
23814
+ function carriesValidModernEnvelopeClaim(params) {
23815
+ if (!hasEnvelopeClaim(params)) return false;
23816
+ const claimedVersion = envelopeClaimVersion(params);
23817
+ if (claimedVersion === void 0 || !isModernProtocolVersion(claimedVersion)) return false;
23818
+ const meta3 = requestMetaOf(params);
23819
+ return meta3 !== void 0 && validateEnvelopeMeta(meta3).length === 0;
23820
+ }
23821
+ function modernOnlyStrictRejection(route, supportedVersions) {
23822
+ switch (route.reason) {
23823
+ case "http-method":
23824
+ return rejection("http-method", "modern-only-method-not-allowed", 405, new ProtocolError(-32e3, "Method not allowed."), true);
23825
+ case "batch":
23826
+ return rejection("jsonrpc-shape", "modern-only-batch-not-supported", 400, new ProtocolError(ProtocolErrorCode.InvalidRequest, "Bad Request: JSON-RPC batches are not supported by this endpoint"), true);
23827
+ case "response":
23828
+ return rejection("jsonrpc-shape", "modern-only-response-post", 400, new ProtocolError(ProtocolErrorCode.InvalidRequest, "Bad Request: JSON-RPC responses cannot be posted to this endpoint"), true);
23829
+ case "notification":
23830
+ return;
23831
+ case "initialize":
23832
+ case "no-claim": {
23833
+ const requested = route.requestedVersion;
23834
+ return rejection("era-classification", "modern-only-missing-envelope", 400, requested === void 0 ? new ProtocolError(ProtocolErrorCode.UnsupportedProtocolVersion, "Unsupported protocol version: the request did not name a protocol version", { supported: [...supportedVersions] }) : new UnsupportedProtocolVersionError({
23835
+ supported: [...supportedVersions],
23836
+ requested
23837
+ }), true);
23838
+ }
23839
+ }
23840
+ }
23756
23841
  function parseSchema(schema, data) {
23757
23842
  return safeParse2(schema, data);
23758
23843
  }
@@ -24314,6 +24399,9 @@ function withRequestStateValue(ctx, value) {
24314
24399
  };
24315
24400
  }
24316
24401
  var writeNegotiatedProtocolVersion;
24402
+ function setNegotiatedProtocolVersion(instance, version2) {
24403
+ writeNegotiatedProtocolVersion(instance, version2);
24404
+ }
24317
24405
  var Protocol = class {
24318
24406
  _transport;
24319
24407
  _requestMessageId = 0;
@@ -32257,6 +32345,190 @@ function getCompleter(schema) {
32257
32345
  return schema[COMPLETABLE_SYMBOL]?.complete;
32258
32346
  }
32259
32347
  var MAX_TIMER_DELAY_MS = 2 ** 31 - 1;
32348
+ function listenFilterAccepts(filter, event) {
32349
+ switch (event.kind) {
32350
+ case "tools_list_changed":
32351
+ return filter.toolsListChanged === true;
32352
+ case "prompts_list_changed":
32353
+ return filter.promptsListChanged === true;
32354
+ case "resources_list_changed":
32355
+ return filter.resourcesListChanged === true;
32356
+ case "resource_updated":
32357
+ return filter.resourceSubscriptions !== void 0 && filter.resourceSubscriptions.includes(event.uri);
32358
+ }
32359
+ }
32360
+ function honoredSubset(requested, capabilities) {
32361
+ const honored = {};
32362
+ const allow = (bit) => capabilities === void 0 || bit === true;
32363
+ if (requested.toolsListChanged === true && allow(capabilities?.tools?.listChanged)) honored.toolsListChanged = true;
32364
+ if (requested.promptsListChanged === true && allow(capabilities?.prompts?.listChanged)) honored.promptsListChanged = true;
32365
+ if (requested.resourcesListChanged === true && allow(capabilities?.resources?.listChanged)) honored.resourcesListChanged = true;
32366
+ if (requested.resourceSubscriptions !== void 0 && requested.resourceSubscriptions.length > 0 && allow(capabilities?.resources?.subscribe)) honored.resourceSubscriptions = [...requested.resourceSubscriptions];
32367
+ return honored;
32368
+ }
32369
+ var DEFAULT_MAX_SUBSCRIPTIONS = 1024;
32370
+ function stampSubscriptionId(notification, subscriptionId) {
32371
+ return {
32372
+ method: notification.method,
32373
+ params: {
32374
+ ...notification.params,
32375
+ _meta: {
32376
+ ...notification.params?._meta,
32377
+ [SUBSCRIPTION_ID_META_KEY]: subscriptionId
32378
+ }
32379
+ }
32380
+ };
32381
+ }
32382
+ function parseListenFilter(message) {
32383
+ const outcome = codecForVersion(MODERN_WIRE_REVISION).validateRequest("subscriptions/listen", message);
32384
+ return outcome.ok ? outcome.value.params?.notifications : void 0;
32385
+ }
32386
+ var CHANGE_NOTIFICATION_METHODS = /* @__PURE__ */ new Set([
32387
+ "notifications/tools/list_changed",
32388
+ "notifications/prompts/list_changed",
32389
+ "notifications/resources/list_changed",
32390
+ "notifications/resources/updated"
32391
+ ]);
32392
+ var StdioListenRouter = class {
32393
+ /** Active subscriptions, keyed by the listen request's JSON-RPC id verbatim. */
32394
+ _subs = /* @__PURE__ */ new Map();
32395
+ /**
32396
+ * The serving instance's declared capabilities. Filled in by the entry
32397
+ * once the modern instance is constructed (the router is created before
32398
+ * the instance exists), so the acknowledged filter is narrowed against
32399
+ * what the server can actually deliver.
32400
+ */
32401
+ _serverCapabilities;
32402
+ /**
32403
+ * The serving instance's identity, stamped onto the graceful-close
32404
+ * results' `_meta` (the spec's `SubscriptionsListenResultMeta` extends
32405
+ * `ResultMetaObject`). Handed over together with the capabilities.
32406
+ */
32407
+ _serverInfo;
32408
+ constructor(_maxSubscriptions = DEFAULT_MAX_SUBSCRIPTIONS, serverCapabilities, serverInfo) {
32409
+ this._maxSubscriptions = _maxSubscriptions;
32410
+ this._serverCapabilities = serverCapabilities;
32411
+ this._serverInfo = serverInfo;
32412
+ }
32413
+ /**
32414
+ * Record the serving instance's declared capabilities and identity once
32415
+ * it has been constructed. Called by `serveStdio`'s connect path;
32416
+ * subsequent `serve()` calls narrow the honored filter against the
32417
+ * capabilities, and `teardownAll()` stamps the identity.
32418
+ */
32419
+ setServerCapabilities(capabilities, serverInfo) {
32420
+ this._serverCapabilities = capabilities;
32421
+ if (serverInfo !== void 0) this._serverInfo = serverInfo;
32422
+ }
32423
+ /** Whether `id` is an active listen subscription on this connection. */
32424
+ has(id) {
32425
+ return this._subs.has(id);
32426
+ }
32427
+ /**
32428
+ * Serve one inbound `subscriptions/listen` request: registers the
32429
+ * subscription and returns the stamped acknowledged notification (or, on
32430
+ * capacity / params rejection, the in-band JSON-RPC error response).
32431
+ *
32432
+ * @throws when called before {@linkcode setServerCapabilities} (or the
32433
+ * constructor) has supplied the serving instance's capabilities. Honoring a
32434
+ * filter without knowing the server's advertised capabilities would fail
32435
+ * open (deliver unadvertised types); the entry guarantees capabilities are
32436
+ * set before any listen request is routed here.
32437
+ */
32438
+ serve(message) {
32439
+ if (this._serverCapabilities === void 0) throw new Error("StdioListenRouter.serve() called before setServerCapabilities(); refusing to honor a filter without capabilities");
32440
+ if (this._subs.size >= this._maxSubscriptions) return {
32441
+ jsonrpc: "2.0",
32442
+ id: message.id,
32443
+ error: {
32444
+ code: -32603,
32445
+ message: "Subscription limit reached"
32446
+ }
32447
+ };
32448
+ const filter = parseListenFilter(message);
32449
+ if (filter === void 0) return {
32450
+ jsonrpc: "2.0",
32451
+ id: message.id,
32452
+ error: {
32453
+ code: -32602,
32454
+ message: "Invalid params: 'notifications' is required and must be a valid SubscriptionFilter"
32455
+ }
32456
+ };
32457
+ const honored = honoredSubset(filter, this._serverCapabilities);
32458
+ this._subs.set(message.id, honored);
32459
+ return stampSubscriptionId({
32460
+ method: "notifications/subscriptions/acknowledged",
32461
+ params: { notifications: honored }
32462
+ }, message.id);
32463
+ }
32464
+ /**
32465
+ * Tear down one subscription (inbound `notifications/cancelled`). Returns
32466
+ * `true` when a subscription was removed. After this call NOTHING further
32467
+ * is delivered for that subscription id (the post-cancel hardening).
32468
+ */
32469
+ cancel(id) {
32470
+ return this._subs.delete(id);
32471
+ }
32472
+ /**
32473
+ * Route an outbound notification through the active subscriptions.
32474
+ *
32475
+ * - For a subscription-gated change notification, returns one stamped copy
32476
+ * per subscription that opted in to it (an empty array means it is
32477
+ * dropped — the modern era never delivers an un-requested change type).
32478
+ * - For any other outbound message, returns `'passthrough'` (the entry
32479
+ * forwards it as-is).
32480
+ */
32481
+ routeOutbound(message) {
32482
+ if (!CHANGE_NOTIFICATION_METHODS.has(message.method)) return "passthrough";
32483
+ const uriParam = message.params?.["uri"];
32484
+ const uri = typeof uriParam === "string" ? uriParam : void 0;
32485
+ const event = notificationToServerEvent(message.method, uri);
32486
+ const out = [];
32487
+ for (const [subscriptionId, filter] of this._subs) if (listenFilterAccepts(filter, event)) out.push(stampSubscriptionId({
32488
+ method: message.method,
32489
+ params: message.params ?? {}
32490
+ }, subscriptionId));
32491
+ return out;
32492
+ }
32493
+ /**
32494
+ * Server-side graceful teardown of every active subscription: returns the
32495
+ * empty `subscriptions/listen` JSON-RPC result for each subscription id —
32496
+ * the spec's graceful-close signal, `_meta` carrying the subscription id
32497
+ * and the serving instance's identity — for the entry to emit before
32498
+ * closing the wire. Clears the set so nothing further is delivered.
32499
+ */
32500
+ teardownAll() {
32501
+ const out = [];
32502
+ for (const id of this._subs.keys()) out.push({
32503
+ jsonrpc: "2.0",
32504
+ id,
32505
+ result: {
32506
+ resultType: "complete",
32507
+ _meta: {
32508
+ [SUBSCRIPTION_ID_META_KEY]: id,
32509
+ ...this._serverInfo !== void 0 && { [SERVER_INFO_META_KEY]: this._serverInfo }
32510
+ }
32511
+ }
32512
+ });
32513
+ this._subs.clear();
32514
+ return out;
32515
+ }
32516
+ };
32517
+ function notificationToServerEvent(method, uri) {
32518
+ switch (method) {
32519
+ case "notifications/tools/list_changed":
32520
+ return { kind: "tools_list_changed" };
32521
+ case "notifications/prompts/list_changed":
32522
+ return { kind: "prompts_list_changed" };
32523
+ case "notifications/resources/list_changed":
32524
+ return { kind: "resources_list_changed" };
32525
+ default:
32526
+ return {
32527
+ kind: "resource_updated",
32528
+ uri: uri ?? ""
32529
+ };
32530
+ }
32531
+ }
32260
32532
  var DEFAULT_LEGACY_SHIM_MAX_ROUNDS = 8;
32261
32533
  var DEFAULT_LEGACY_SHIM_ROUND_TIMEOUT_MS = 6e5;
32262
32534
  function resolveLegacyShimOptions(options) {
@@ -32394,6 +32666,12 @@ var INPUT_REQUIRED_CAPABLE_METHODS = /* @__PURE__ */ new Set([
32394
32666
  var writeClientIdentity;
32395
32667
  var installDiscoverHandler;
32396
32668
  var readServerIdentity;
32669
+ function installModernOnlyHandlers(server, servedModernVersions) {
32670
+ installDiscoverHandler(server, servedModernVersions);
32671
+ }
32672
+ function serverIdentityOf(server) {
32673
+ return readServerIdentity(server);
32674
+ }
32397
32675
  var Server = class extends Protocol {
32398
32676
  _clientCapabilities;
32399
32677
  _clientVersion;
@@ -33668,6 +33946,400 @@ var StdioServerTransport = class {
33668
33946
  });
33669
33947
  }
33670
33948
  };
33949
+ var DISCARD_ANSWER_TIMEOUT_MS = 3e3;
33950
+ var StdioConnectionChannel = class {
33951
+ onclose;
33952
+ onerror;
33953
+ onmessage;
33954
+ _closed = false;
33955
+ /** Request ids the entry delivered to the instance that the instance has not yet answered. */
33956
+ _pendingRequests = /* @__PURE__ */ new Set();
33957
+ _drainWaiters = [];
33958
+ constructor(_wire, _onInstanceClose, _outboundIntercept) {
33959
+ this._wire = _wire;
33960
+ this._onInstanceClose = _onInstanceClose;
33961
+ this._outboundIntercept = _outboundIntercept;
33962
+ }
33963
+ async start() {
33964
+ }
33965
+ async send(message, options) {
33966
+ if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {
33967
+ const { id } = message;
33968
+ if (id !== void 0) this._settle(id);
33969
+ }
33970
+ if (this._closed) return;
33971
+ if (this._outboundIntercept?.(message) === "handled") return;
33972
+ return this._wire.send(message, options);
33973
+ }
33974
+ setProtocolVersion = (version2) => {
33975
+ this._wire.setProtocolVersion?.(version2);
33976
+ };
33977
+ /** Forwards one inbound message to the connected instance. */
33978
+ deliver(message, extra) {
33979
+ if (this._closed) return;
33980
+ if (isJSONRPCRequest(message)) this._pendingRequests.add(message.id);
33981
+ else if (isJSONRPCNotification(message) && message.method === "notifications/cancelled") {
33982
+ const cancelledId = message.params?.requestId;
33983
+ if (cancelledId !== void 0) this._settle(cancelledId);
33984
+ }
33985
+ this.onmessage?.(message, extra);
33986
+ }
33987
+ /**
33988
+ * Resolves once every request delivered to the instance has been answered
33989
+ * through {@linkcode send}, settled by a delivered cancellation, or the
33990
+ * channel has been closed and nothing further can be answered. The wait is
33991
+ * bounded by `timeoutMs` as a backstop so no edge can hold the caller
33992
+ * indefinitely; resolves `false` only when the bound elapsed with requests
33993
+ * still unanswered. Used by the probe-discard path so a probe request the
33994
+ * entry accepted is never silently dropped.
33995
+ */
33996
+ async whenRequestsAnswered(timeoutMs) {
33997
+ if (this._closed || this._pendingRequests.size === 0) return true;
33998
+ return await new Promise((resolve) => {
33999
+ const waiter = () => {
34000
+ clearTimeout(timer);
34001
+ resolve(true);
34002
+ };
34003
+ const timer = setTimeout(() => {
34004
+ this._drainWaiters = this._drainWaiters.filter((pending) => pending !== waiter);
34005
+ resolve(false);
34006
+ }, timeoutMs);
34007
+ this._drainWaiters.push(waiter);
34008
+ });
34009
+ }
34010
+ async close() {
34011
+ if (this._closed) return;
34012
+ this._closed = true;
34013
+ this._pendingRequests.clear();
34014
+ this._releaseDrainWaiters();
34015
+ try {
34016
+ this._onInstanceClose();
34017
+ } finally {
34018
+ this.onclose?.();
34019
+ }
34020
+ }
34021
+ _settle(id) {
34022
+ this._pendingRequests.delete(id);
34023
+ if (this._pendingRequests.size === 0) this._releaseDrainWaiters();
34024
+ }
34025
+ _releaseDrainWaiters() {
34026
+ const waiters = this._drainWaiters;
34027
+ this._drainWaiters = [];
34028
+ for (const waiter of waiters) waiter();
34029
+ }
34030
+ };
34031
+ function classifyOpeningMessage(message) {
34032
+ const params = message.params;
34033
+ if (message.method === "initialize" && !carriesValidModernEnvelopeClaim(params)) {
34034
+ const requestedVersion = params !== null && typeof params === "object" && typeof params.protocolVersion === "string" ? params.protocolVersion : void 0;
34035
+ return {
34036
+ kind: "legacy",
34037
+ reason: "initialize",
34038
+ ...requestedVersion !== void 0 && { requestedVersion }
34039
+ };
34040
+ }
34041
+ if (!hasEnvelopeClaim(params)) return {
34042
+ kind: "legacy",
34043
+ reason: "no-claim"
34044
+ };
34045
+ const meta3 = requestMetaOf(params);
34046
+ const firstIssue = (meta3 === void 0 ? [] : validateEnvelopeMeta(meta3))[0];
34047
+ if (firstIssue !== void 0) return {
34048
+ kind: "invalid-envelope",
34049
+ issue: firstIssue
34050
+ };
34051
+ const claimedVersion = envelopeClaimVersion(params);
34052
+ if (claimedVersion === void 0 || !SUPPORTED_MODERN_PROTOCOL_VERSIONS.includes(claimedVersion)) return {
34053
+ kind: "unsupported-revision",
34054
+ requested: claimedVersion ?? "unknown"
34055
+ };
34056
+ return {
34057
+ kind: "modern",
34058
+ revision: claimedVersion,
34059
+ classification: {
34060
+ era: "modern",
34061
+ revision: claimedVersion
34062
+ }
34063
+ };
34064
+ }
34065
+ function serveStdio(factory, options = {}) {
34066
+ const legacyMode = options.legacy ?? "serve";
34067
+ const wire = options.transport ?? new StdioServerTransport();
34068
+ let state = { phase: "opening" };
34069
+ let discarding;
34070
+ let closing = false;
34071
+ const isTornDown = () => closing || state.phase === "closed";
34072
+ const reportError = (error62) => {
34073
+ try {
34074
+ options.onerror?.(error62);
34075
+ } catch {
34076
+ }
34077
+ };
34078
+ const writeErrorResponse = (id, code, message, data) => wire.send({
34079
+ jsonrpc: "2.0",
34080
+ id,
34081
+ error: {
34082
+ code,
34083
+ message,
34084
+ ...data !== void 0 && { data }
34085
+ }
34086
+ }).catch((error62) => reportError(toError(error62)));
34087
+ const listenRouter = new StdioListenRouter(options.maxSubscriptions ?? DEFAULT_MAX_SUBSCRIPTIONS);
34088
+ const modernOutboundIntercept = (message) => {
34089
+ if (!isJSONRPCNotification(message)) return void 0;
34090
+ const routed = listenRouter.routeOutbound(message);
34091
+ if (routed === "passthrough") return void 0;
34092
+ for (const stamped of routed) wire.send({
34093
+ jsonrpc: "2.0",
34094
+ ...stamped
34095
+ }).catch((error62) => reportError(toError(error62)));
34096
+ return "handled";
34097
+ };
34098
+ const tryServeListen = async (message) => {
34099
+ if (isJSONRPCRequest(message) && message.method === "subscriptions/listen") {
34100
+ const meta3 = requestMetaOf(message.params);
34101
+ const issue2 = hasEnvelopeClaim(message.params) ? (meta3 === void 0 ? [] : validateEnvelopeMeta(meta3))[0] : {
34102
+ key: "_meta",
34103
+ problem: "the per-request envelope is required on protocol revision 2026-07-28"
34104
+ };
34105
+ const claimedVersion = envelopeClaimVersion(message.params);
34106
+ let reply;
34107
+ if (issue2 !== void 0) reply = {
34108
+ jsonrpc: "2.0",
34109
+ id: message.id,
34110
+ error: {
34111
+ code: -32602,
34112
+ message: `Invalid _meta envelope: ${issue2.key}: ${issue2.problem}`
34113
+ }
34114
+ };
34115
+ else if (claimedVersion === void 0 || !SUPPORTED_MODERN_PROTOCOL_VERSIONS.includes(claimedVersion)) {
34116
+ const error62 = new UnsupportedProtocolVersionError({
34117
+ supported: [...SUPPORTED_MODERN_PROTOCOL_VERSIONS],
34118
+ requested: claimedVersion ?? "unknown"
34119
+ });
34120
+ reply = {
34121
+ jsonrpc: "2.0",
34122
+ id: message.id,
34123
+ error: {
34124
+ code: error62.code,
34125
+ message: error62.message,
34126
+ data: error62.data
34127
+ }
34128
+ };
34129
+ } else reply = listenRouter.serve(message);
34130
+ await wire.send("error" in reply ? reply : {
34131
+ jsonrpc: "2.0",
34132
+ method: reply.method,
34133
+ params: reply.params
34134
+ }).catch((error62) => reportError(toError(error62)));
34135
+ return true;
34136
+ }
34137
+ if (isJSONRPCNotification(message) && message.method === "notifications/cancelled") {
34138
+ const cancelledId = message.params?.requestId;
34139
+ if (cancelledId !== void 0 && listenRouter.cancel(cancelledId)) return true;
34140
+ }
34141
+ return false;
34142
+ };
34143
+ const answerLegacyRejection = (request, reason, requestedVersion) => {
34144
+ const rejection2 = modernOnlyStrictRejection({
34145
+ kind: "legacy",
34146
+ reason,
34147
+ ...requestedVersion !== void 0 && { requestedVersion }
34148
+ }, SUPPORTED_MODERN_PROTOCOL_VERSIONS);
34149
+ if (rejection2 === void 0) return Promise.resolve();
34150
+ reportError(/* @__PURE__ */ new Error(`Rejected 2025-era request on a modern-only stdio connection (${rejection2.cell}): ${rejection2.message}`));
34151
+ return writeErrorResponse(request.id, rejection2.code, rejection2.message, rejection2.data);
34152
+ };
34153
+ const onInstanceClosed = (channel) => {
34154
+ if (closing || channel === discarding) return;
34155
+ closeAll();
34156
+ };
34157
+ const connectInstance = async (era, revision) => {
34158
+ const product = await factory({ era });
34159
+ const server = product instanceof McpServer ? product.server : product;
34160
+ if (era === "modern") {
34161
+ setNegotiatedProtocolVersion(server, revision);
34162
+ installModernOnlyHandlers(server, SUPPORTED_MODERN_PROTOCOL_VERSIONS);
34163
+ listenRouter.setServerCapabilities(server.getCapabilities(), serverIdentityOf(server));
34164
+ }
34165
+ const channel = new StdioConnectionChannel(wire, () => onInstanceClosed(channel), era === "modern" ? modernOutboundIntercept : void 0);
34166
+ await product.connect(channel);
34167
+ return {
34168
+ product,
34169
+ channel
34170
+ };
34171
+ };
34172
+ const disposeLateInstance = (instance) => instance.product.close().catch((error62) => reportError(toError(error62)));
34173
+ const discardProbeInstance = async (instance) => {
34174
+ discarding = instance.channel;
34175
+ try {
34176
+ if (!await instance.channel.whenRequestsAnswered(DISCARD_ANSWER_TIMEOUT_MS)) reportError(/* @__PURE__ */ new Error(`Discarded the probe instance with requests still unanswered after ${DISCARD_ANSWER_TIMEOUT_MS}ms; continuing with the fallback`));
34177
+ await instance.product.close();
34178
+ } catch (error62) {
34179
+ reportError(toError(error62));
34180
+ } finally {
34181
+ discarding = void 0;
34182
+ }
34183
+ };
34184
+ const processMessage = async (message) => {
34185
+ if (state.phase === "closed") return;
34186
+ if (state.phase === "pinned") {
34187
+ if (state.era === "modern" && isJSONRPCRequest(message) && message.method === "initialize" && !carriesValidModernEnvelopeClaim(message.params)) {
34188
+ await answerLegacyRejection(message, "initialize", message.params !== null && typeof message.params === "object" && typeof message.params.protocolVersion === "string" ? message.params.protocolVersion : void 0);
34189
+ return;
34190
+ }
34191
+ if (state.era === "modern" && await tryServeListen(message)) return;
34192
+ state.instance.channel.deliver(message);
34193
+ return;
34194
+ }
34195
+ if (!isJSONRPCRequest(message) && !isJSONRPCNotification(message)) {
34196
+ reportError(/* @__PURE__ */ new Error("Discarded a JSON-RPC response received before the connection negotiated an era"));
34197
+ return;
34198
+ }
34199
+ const opening = classifyOpeningMessage(message);
34200
+ switch (opening.kind) {
34201
+ case "invalid-envelope": {
34202
+ const detail = `Invalid _meta envelope for protocol revision 2026-07-28: ${opening.issue.key}: ${opening.issue.problem}`;
34203
+ if (isJSONRPCRequest(message)) await writeErrorResponse(message.id, ProtocolErrorCode.InvalidParams, detail, { envelope: opening.issue });
34204
+ else reportError(/* @__PURE__ */ new Error(`Discarded a notification with a malformed envelope: ${detail}`));
34205
+ return;
34206
+ }
34207
+ case "unsupported-revision":
34208
+ if (isJSONRPCRequest(message)) {
34209
+ const error62 = new UnsupportedProtocolVersionError({
34210
+ supported: [...SUPPORTED_MODERN_PROTOCOL_VERSIONS],
34211
+ requested: opening.requested
34212
+ });
34213
+ reportError(error62);
34214
+ await writeErrorResponse(message.id, error62.code, error62.message, error62.data);
34215
+ } else reportError(/* @__PURE__ */ new Error(`Discarded a notification claiming unsupported protocol revision ${opening.requested}`));
34216
+ return;
34217
+ case "modern":
34218
+ if (isJSONRPCRequest(message) && message.method === "server/discover") {
34219
+ if (state.phase === "probe") {
34220
+ state.instance.channel.deliver(message, { classification: opening.classification });
34221
+ return;
34222
+ }
34223
+ const instance = await connectInstance("modern", opening.revision);
34224
+ if (isTornDown()) {
34225
+ await disposeLateInstance(instance);
34226
+ return;
34227
+ }
34228
+ state = {
34229
+ phase: "probe",
34230
+ instance
34231
+ };
34232
+ instance.channel.deliver(message, { classification: opening.classification });
34233
+ return;
34234
+ }
34235
+ if (state.phase === "probe") {
34236
+ if (isJSONRPCNotification(message)) {
34237
+ state.instance.channel.deliver(message, { classification: opening.classification });
34238
+ return;
34239
+ }
34240
+ state = {
34241
+ phase: "pinned",
34242
+ era: "modern",
34243
+ instance: state.instance
34244
+ };
34245
+ } else {
34246
+ const instance = await connectInstance("modern", opening.revision);
34247
+ if (isTornDown()) {
34248
+ await disposeLateInstance(instance);
34249
+ return;
34250
+ }
34251
+ state = {
34252
+ phase: "pinned",
34253
+ era: "modern",
34254
+ instance
34255
+ };
34256
+ }
34257
+ if (await tryServeListen(message)) return;
34258
+ state.instance.channel.deliver(message, { classification: opening.classification });
34259
+ return;
34260
+ case "legacy": {
34261
+ if (legacyMode === "reject") {
34262
+ if (isJSONRPCRequest(message)) await answerLegacyRejection(message, opening.reason, opening.requestedVersion);
34263
+ return;
34264
+ }
34265
+ if (state.phase === "probe") {
34266
+ await discardProbeInstance(state.instance);
34267
+ if (isTornDown()) return;
34268
+ state = { phase: "opening" };
34269
+ }
34270
+ const instance = await connectInstance("legacy");
34271
+ if (isTornDown()) {
34272
+ await disposeLateInstance(instance);
34273
+ return;
34274
+ }
34275
+ state = {
34276
+ phase: "pinned",
34277
+ era: "legacy",
34278
+ instance
34279
+ };
34280
+ state.instance.channel.deliver(message);
34281
+ return;
34282
+ }
34283
+ }
34284
+ };
34285
+ const queue = [];
34286
+ let pumping = false;
34287
+ const pump = async () => {
34288
+ if (pumping) return;
34289
+ pumping = true;
34290
+ try {
34291
+ while (queue.length > 0) {
34292
+ const message = queue.shift();
34293
+ try {
34294
+ await processMessage(message);
34295
+ } catch (error62) {
34296
+ if (isJSONRPCRequest(message)) await writeErrorResponse(message.id, ProtocolErrorCode.InternalError, "Internal server error");
34297
+ reportError(toError(error62));
34298
+ }
34299
+ }
34300
+ } finally {
34301
+ pumping = false;
34302
+ }
34303
+ };
34304
+ const closeAll = async () => {
34305
+ if (closing || state.phase === "closed") return;
34306
+ closing = true;
34307
+ const current = state;
34308
+ state = { phase: "closed" };
34309
+ for (const result of listenRouter.teardownAll()) await wire.send(result).catch((error62) => reportError(toError(error62)));
34310
+ if (current.phase === "probe" || current.phase === "pinned") await current.instance.product.close().catch((error62) => reportError(toError(error62)));
34311
+ await wire.close().catch((error62) => reportError(toError(error62)));
34312
+ };
34313
+ wire.onmessage = (message) => {
34314
+ queue.push(message);
34315
+ pump();
34316
+ };
34317
+ wire.onerror = (error62) => {
34318
+ reportError(error62);
34319
+ if (state.phase === "probe" || state.phase === "pinned") state.instance.channel.onerror?.(error62);
34320
+ };
34321
+ wire.onclose = () => {
34322
+ if (closing || state.phase === "closed") return;
34323
+ closing = true;
34324
+ const current = state;
34325
+ state = { phase: "closed" };
34326
+ if (current.phase === "probe" || current.phase === "pinned") current.instance.product.close().catch((error62) => reportError(toError(error62)));
34327
+ };
34328
+ const started = wire.start().catch((error62) => {
34329
+ reportError(toError(error62));
34330
+ throw error62;
34331
+ });
34332
+ started.catch(() => {
34333
+ });
34334
+ return { close: async () => {
34335
+ await started.catch(() => {
34336
+ });
34337
+ await closeAll();
34338
+ } };
34339
+ }
34340
+ function toError(value) {
34341
+ return value instanceof Error ? value : new Error(String(value));
34342
+ }
33671
34343
 
33672
34344
  // ../../node_modules/@chrischall/mcp-utils/dist/errors/index.js
33673
34345
  var McpToolError = class extends Error {
@@ -33808,17 +34480,41 @@ Hint: ${err.hint}`);
33808
34480
  }
33809
34481
  throw err;
33810
34482
  }
34483
+ function callSignalFrom(args) {
34484
+ const ctx = args.at(-1);
34485
+ if (typeof ctx !== "object" || ctx === null)
34486
+ return void 0;
34487
+ const req = ctx.mcpReq;
34488
+ if (typeof req !== "object" || req === null)
34489
+ return void 0;
34490
+ const signal = req.signal;
34491
+ return signal instanceof AbortSignal ? signal : void 0;
34492
+ }
33811
34493
  function surfaceToolHints(server) {
33812
34494
  const register2 = server.registerTool.bind(server);
33813
- server.registerTool = (name, config2, cb) => register2(name, config2, (...args) => {
33814
- let result;
33815
- try {
33816
- result = cb(...args);
33817
- } catch (err) {
33818
- return hintResultOrRethrow(err);
33819
- }
33820
- return result instanceof Promise ? result.catch(hintResultOrRethrow) : result;
33821
- });
34495
+ server.registerTool = (name, config2, cb) => register2(name, config2, (...args) => (
34496
+ // THE CALLER'S CANCELLATION, made ambient for the whole handler
34497
+ // (`cancel/index.ts`). The SDK delivers it and the fleet ignored it:
34498
+ // measured against @modelcontextprotocol/server 2.0.0, a cancelled
34499
+ // call aborts `ctx.mcpReq.signal` with the caller's reason and the
34500
+ // handler runs to completion regardless — so the HTTP request stays
34501
+ // in flight, the child keeps burning metered CPU, and the upstream
34502
+ // keeps being hit for somebody who has gone. claude.ai sent 101
34503
+ // cancellations in the week to 2026-09-20.
34504
+ //
34505
+ // Here because this is the one wrapper every tool already passes
34506
+ // through: threading the signal by hand would mean editing several
34507
+ // hundred handlers and missing exactly the ones nobody edits.
34508
+ withCallSignal(callSignalFrom(args), () => {
34509
+ let result;
34510
+ try {
34511
+ result = cb(...args);
34512
+ } catch (err) {
34513
+ return hintResultOrRethrow(err);
34514
+ }
34515
+ return result instanceof Promise ? result.catch(hintResultOrRethrow) : result;
34516
+ })
34517
+ ));
33822
34518
  }
33823
34519
  async function createMcpServer(opts) {
33824
34520
  const server = new McpServer({ name: opts.name, version: opts.version }, { supportedProtocolVersions: [...SERVER_PROTOCOL_VERSIONS] });
@@ -33833,7 +34529,7 @@ async function createMcpServer(opts) {
33833
34529
  }
33834
34530
  return server;
33835
34531
  }
33836
- function withGracefulShutdown(server, opts = {}) {
34532
+ function withGracefulShutdown(target, opts = {}) {
33837
34533
  const shouldExit = opts.exit ?? true;
33838
34534
  let shuttingDown = false;
33839
34535
  const handler = (signal) => {
@@ -33844,7 +34540,7 @@ function withGracefulShutdown(server, opts = {}) {
33844
34540
  try {
33845
34541
  if (opts.onSignal)
33846
34542
  await opts.onSignal(signal);
33847
- await server.close();
34543
+ await target.close();
33848
34544
  } catch (err) {
33849
34545
  console.error(`[mcp-utils] error during graceful shutdown on ${signal}: ${err instanceof Error ? err.message : String(err)}`);
33850
34546
  } finally {
@@ -33856,16 +34552,26 @@ function withGracefulShutdown(server, opts = {}) {
33856
34552
  process.on("SIGINT", () => handler("SIGINT"));
33857
34553
  process.on("SIGTERM", () => handler("SIGTERM"));
33858
34554
  }
33859
- async function runMcp(opts) {
33860
- const server = await createMcpServer(opts);
34555
+ function runMcp(opts) {
34556
+ if (opts.banner !== void 0) {
34557
+ console.error(opts.banner);
34558
+ }
34559
+ const spec = opts.transport ?? "stdio";
34560
+ const handle = serveStdio(() => createMcpServer({ ...opts, banner: void 0 }), {
34561
+ legacy: opts.legacy ?? "serve",
34562
+ onerror: opts.onerror ?? ((error62) => {
34563
+ console.error(`[mcp-utils] stdio server error: ${redactSecrets(error62.message)}`);
34564
+ }),
34565
+ // `'stdio'` means "let the entry make its own StdioServerTransport over
34566
+ // this process"; anything else is the caller's transport, which the entry
34567
+ // then owns (it starts it, pumps it, and closes it with the handle).
34568
+ ...spec === "stdio" ? {} : { transport: spec }
34569
+ });
33861
34570
  const shutdown = opts.shutdown ?? true;
33862
34571
  if (shutdown !== false) {
33863
- withGracefulShutdown(server, shutdown === true ? {} : shutdown);
34572
+ withGracefulShutdown(handle, shutdown === true ? {} : shutdown);
33864
34573
  }
33865
- const spec = opts.transport ?? "stdio";
33866
- const transport = spec === "stdio" ? new StdioServerTransport() : spec;
33867
- await server.connect(transport);
33868
- return server;
34574
+ return handle;
33869
34575
  }
33870
34576
 
33871
34577
  // ../../node_modules/@chrischall/mcp-utils/dist/config/index.js
@@ -34243,8 +34949,15 @@ async function spawnGog(fullArgs, opts) {
34243
34949
  const stdoutChunks = [];
34244
34950
  const stderrChunks = [];
34245
34951
  let settled = false;
34952
+ const cancelled = currentCallSignal();
34953
+ const stopWatchingCancel = killOnCancel(child);
34954
+ const stopWatching = () => {
34955
+ clearTimeout(timer);
34956
+ stopWatchingCancel();
34957
+ };
34246
34958
  const timer = setTimeout(() => {
34247
34959
  settled = true;
34960
+ stopWatchingCancel();
34248
34961
  child.kill();
34249
34962
  reject(new Error(`gog timed out after ${formatTimeout(effectiveTimeout)}`));
34250
34963
  }, effectiveTimeout);
@@ -34255,9 +34968,13 @@ async function spawnGog(fullArgs, opts) {
34255
34968
  stderrChunks.push(chunk);
34256
34969
  });
34257
34970
  child.on("close", (code) => {
34258
- clearTimeout(timer);
34971
+ stopWatching();
34259
34972
  if (settled) return;
34260
34973
  settled = true;
34974
+ if (cancelled?.aborted) {
34975
+ reject(cancelled.reason instanceof Error ? cancelled.reason : new Error("the caller cancelled this call"));
34976
+ return;
34977
+ }
34261
34978
  const stderr = Buffer.concat(stderrChunks).toString().trim();
34262
34979
  if (code === 0) {
34263
34980
  if (binary) {
@@ -34275,7 +34992,7 @@ async function spawnGog(fullArgs, opts) {
34275
34992
  }
34276
34993
  });
34277
34994
  child.on("error", (err) => {
34278
- clearTimeout(timer);
34995
+ stopWatching();
34279
34996
  if (settled) return;
34280
34997
  settled = true;
34281
34998
  if (err.code === "ENOENT") {
@@ -34996,7 +35713,7 @@ var failIfNotEmptyParam = external_exports.boolean().optional().describe(
34996
35713
  );
34997
35714
 
34998
35715
  // ../gogcli-mcp/src/server.ts
34999
- var VERSION = true ? "4.0.1" : "0.0.0";
35716
+ var VERSION = true ? "4.2.0" : "0.0.0";
35000
35717
 
35001
35718
  // ../gogcli-mcp/src/bootstrap-auth.ts
35002
35719
  import { join as join2 } from "node:path";
package/manifest.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "manifest_version": "0.3",
4
4
  "name": "gogcli-mcp-drive",
5
5
  "display_name": "gogcli (Drive)",
6
- "version": "4.0.1",
6
+ "version": "4.2.0",
7
7
  "description": "Extended Google Drive for Claude via gogcli — auth + full Drive support (upload, download, permissions, comments, shared drives)",
8
8
  "author": {
9
9
  "name": "Chris Hall",
@@ -265,7 +265,7 @@
265
265
  "win32"
266
266
  ],
267
267
  "runtimes": {
268
- "node": ">=18.0.0"
268
+ "node": ">=22.0.0"
269
269
  }
270
270
  }
271
271
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gogcli-mcp-drive",
3
- "version": "4.0.1",
3
+ "version": "4.2.0",
4
4
  "mcpName": "io.github.chrischall/gogcli-mcp-drive",
5
5
  "description": "Extended Google Drive MCP server via gogcli — auth + full Drive support (upload/download/permissions/comments/shared drives)",
6
6
  "author": "Claude Code (AI) <https://www.anthropic.com/claude>",
@@ -24,7 +24,7 @@
24
24
  "test:coverage": "vitest run --coverage"
25
25
  },
26
26
  "dependencies": {
27
- "@chrischall/mcp-utils": "^0.28.0",
27
+ "@chrischall/mcp-utils": "^2.0.0",
28
28
  "@modelcontextprotocol/server": "^2.0.0",
29
29
  "zod": "^4.6.1"
30
30
  },
package/server.json CHANGED
@@ -7,12 +7,12 @@
7
7
  "source": "github",
8
8
  "subfolder": "packages/gogcli-mcp-drive"
9
9
  },
10
- "version": "4.0.1",
10
+ "version": "4.2.0",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "gogcli-mcp-drive",
15
- "version": "4.0.1",
15
+ "version": "4.2.0",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  },