gogcli-mcp-drive 2.19.2 → 2.21.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": "2.19.2"
10
+ "version": "2.21.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": "2.19.2",
18
+ "version": "2.21.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": "2.19.2",
4
+ "version": "2.21.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/dist/index.js CHANGED
@@ -4264,8 +4264,8 @@ var require_core = __commonJS({
4264
4264
  return this;
4265
4265
  }
4266
4266
  case "object": {
4267
- const cacheKey = schemaKeyRef;
4268
- this._cache.delete(cacheKey);
4267
+ const cacheKey2 = schemaKeyRef;
4268
+ this._cache.delete(cacheKey2);
4269
4269
  let id = schemaKeyRef[this.opts.schemaId];
4270
4270
  if (id) {
4271
4271
  id = (0, resolve_1.normalizeId)(id);
@@ -23068,17 +23068,33 @@ function normalizeObjectSchema(schema) {
23068
23068
  }
23069
23069
  return void 0;
23070
23070
  }
23071
+ function getDotPath(path) {
23072
+ if (path.length === 0) {
23073
+ return "object root";
23074
+ }
23075
+ return path.reduce((acc, seg, index) => {
23076
+ if (index === 0) {
23077
+ return String(seg);
23078
+ }
23079
+ if (typeof seg === "number") {
23080
+ return `${acc}[${seg}]`;
23081
+ }
23082
+ return `${acc}.${seg}`;
23083
+ }, "");
23084
+ }
23071
23085
  function getParseErrorMessage(error51) {
23072
23086
  if (error51 && typeof error51 === "object") {
23087
+ if ("issues" in error51 && Array.isArray(error51.issues) && error51.issues.length > 0) {
23088
+ return error51.issues.map((i) => {
23089
+ if (!i.path?.length) {
23090
+ return i.message;
23091
+ }
23092
+ return `${i.message} at ${getDotPath(i.path)}`;
23093
+ }).join("\n");
23094
+ }
23073
23095
  if ("message" in error51 && typeof error51.message === "string") {
23074
23096
  return error51.message;
23075
23097
  }
23076
- if ("issues" in error51 && Array.isArray(error51.issues) && error51.issues.length > 0) {
23077
- const firstIssue = error51.issues[0];
23078
- if (firstIssue && typeof firstIssue === "object" && "message" in firstIssue) {
23079
- return String(firstIssue.message);
23080
- }
23081
- }
23082
23098
  try {
23083
23099
  return JSON.stringify(error51);
23084
23100
  } catch {
@@ -29693,16 +29709,7 @@ var Server = class extends Protocol {
29693
29709
  if (!methodSchema) {
29694
29710
  throw new Error("Schema is missing a method literal");
29695
29711
  }
29696
- let methodValue;
29697
- if (isZ4Schema(methodSchema)) {
29698
- const v4Schema = methodSchema;
29699
- const v4Def = v4Schema._zod?.def;
29700
- methodValue = v4Def?.value ?? v4Schema.value;
29701
- } else {
29702
- const v3Schema = methodSchema;
29703
- const legacyDef = v3Schema._def;
29704
- methodValue = legacyDef?.value ?? v3Schema.value;
29705
- }
29712
+ const methodValue = getLiteralValue(methodSchema);
29706
29713
  if (typeof methodValue !== "string") {
29707
29714
  throw new Error("Schema method literal must be a string");
29708
29715
  }
@@ -30890,8 +30897,17 @@ var EMPTY_COMPLETION_RESULT = {
30890
30897
  import process3 from "node:process";
30891
30898
 
30892
30899
  // ../../node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
30900
+ var STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
30893
30901
  var ReadBuffer = class {
30902
+ constructor(options) {
30903
+ this._maxBufferSize = options?.maxBufferSize ?? STDIO_DEFAULT_MAX_BUFFER_SIZE;
30904
+ }
30894
30905
  append(chunk) {
30906
+ const newSize = (this._buffer?.length ?? 0) + chunk.length;
30907
+ if (newSize > this._maxBufferSize) {
30908
+ this.clear();
30909
+ throw new Error(`ReadBuffer exceeded maximum size of ${this._maxBufferSize} bytes`);
30910
+ }
30895
30911
  this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
30896
30912
  }
30897
30913
  readMessage() {
@@ -30919,18 +30935,24 @@ function serializeMessage(message) {
30919
30935
 
30920
30936
  // ../../node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
30921
30937
  var StdioServerTransport = class {
30922
- constructor(_stdin = process3.stdin, _stdout = process3.stdout) {
30938
+ constructor(_stdin = process3.stdin, _stdout = process3.stdout, options) {
30923
30939
  this._stdin = _stdin;
30924
30940
  this._stdout = _stdout;
30925
- this._readBuffer = new ReadBuffer();
30926
30941
  this._started = false;
30927
30942
  this._ondata = (chunk) => {
30928
- this._readBuffer.append(chunk);
30929
- this.processReadBuffer();
30943
+ try {
30944
+ this._readBuffer.append(chunk);
30945
+ this.processReadBuffer();
30946
+ } catch (error51) {
30947
+ this.onerror?.(error51);
30948
+ this.close().catch(() => {
30949
+ });
30950
+ }
30930
30951
  };
30931
30952
  this._onerror = (error51) => {
30932
30953
  this.onerror?.(error51);
30933
30954
  };
30955
+ this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
30934
30956
  }
30935
30957
  /**
30936
30958
  * Starts listening for messages on stdin.
@@ -31565,7 +31587,9 @@ function registerRunTool(server, options) {
31565
31587
  function errorText(err) {
31566
31588
  return err instanceof Error ? `Error: ${err.message}` : String(err);
31567
31589
  }
31568
- var AUTH_ERROR_PATTERN = /\b(401|unauthorized|token.*(expired|revoked)|invalid_grant)\b/i;
31590
+ var DEFINITE_AUTH_PATTERN = /\b(401|unauthorized|invalid_grant)\b/i;
31591
+ var STALE_TOKEN_PATTERN = /\b(?:access[ _-]?)?token\b[^.;\n]{0,40}\b(?:has\s+)?(?:been\s+)?(?:expired|revoked)\b|\b(?:expired|revoked)\s+(?:access[ _-]?)?token\b/i;
31592
+ var AUTH_ERROR_PATTERN = new RegExp(`${DEFINITE_AUTH_PATTERN.source}|${STALE_TOKEN_PATTERN.source}`, "i");
31569
31593
  var INVALID_GRANT_PATTERN = /invalid_grant|token has been expired or revoked/i;
31570
31594
  var TRANSIENT_ERROR_PATTERN = /\b429\b|\b5\d\d\b|\bquota\b|rateLimit|\bDEADLINE_EXCEEDED\b/i;
31571
31595
  var GRID_LIMIT_ERROR_PATTERN = /exceeds grid limits/i;
@@ -31586,8 +31610,8 @@ function formatAccountList(raw) {
31586
31610
  async function diagnose(err) {
31587
31611
  const errText = errorText(err);
31588
31612
  const isInvalidGrant = INVALID_GRANT_PATTERN.test(errText);
31589
- const isAuthError = AUTH_ERROR_PATTERN.test(errText);
31590
- const isTransientError = !isAuthError && TRANSIENT_ERROR_PATTERN.test(errText);
31613
+ const isTransientError = !DEFINITE_AUTH_PATTERN.test(errText) && TRANSIENT_ERROR_PATTERN.test(errText);
31614
+ const isAuthError = !isTransientError && AUTH_ERROR_PATTERN.test(errText);
31591
31615
  const isGridLimitError = GRID_LIMIT_ERROR_PATTERN.test(errText);
31592
31616
  const hint = isInvalidGrant ? INVALID_GRANT_HINT : isAuthError ? AUTH_HINT : isTransientError ? TRANSIENT_HINT : isGridLimitError ? GRID_LIMIT_HINT : "";
31593
31617
  try {
@@ -31654,7 +31678,7 @@ function formatAuthHealth(raw, now) {
31654
31678
  function registerAuthToolsWith(server, defaultServices) {
31655
31679
  const servicesDescribe = `Services to authorize: "all" or comma-separated list (e.g. "sheets,gmail,calendar"). Default: "${defaultServices}". Prefer the narrowest set you need \u2014 requesting a service whose Google API is not enabled on the OAuth client's project makes Google reject the WHOLE request with invalid_scope.`;
31656
31680
  server.registerTool("gog_auth_list", {
31657
- description: "List all Google accounts stored in gogcli. Use this to check which accounts are configured and available.",
31681
+ description: "List the Google accounts stored in gogcli, with their scopes. This reads local configuration only \u2014 it does not contact Google and does NOT tell you whether an account still works: a signed-out account whose refresh token expired or was revoked is listed here exactly like a healthy one, scopes and all. Use gog_auth_health to check whether an account can actually authenticate.",
31658
31682
  annotations: { readOnlyHint: true },
31659
31683
  inputSchema: {}
31660
31684
  }, async () => {
@@ -31997,16 +32021,94 @@ var failIfNotEmptyParam = external_exports.boolean().optional().describe(
31997
32021
  );
31998
32022
 
31999
32023
  // ../gogcli-mcp/src/server.ts
32000
- var VERSION = true ? "2.19.2" : "0.0.0";
32024
+ var VERSION = true ? "2.21.0" : "0.0.0";
32025
+
32026
+ // ../gogcli-mcp/src/google-token.ts
32027
+ var TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token";
32028
+ var EXPIRY_MARGIN_MS = 12e4;
32029
+ var cache = /* @__PURE__ */ new Map();
32030
+ var inFlight = /* @__PURE__ */ new Map();
32031
+ async function cacheKey(refreshToken, clientId) {
32032
+ const data = new TextEncoder().encode(`${clientId}\0${refreshToken}`);
32033
+ const digest = await crypto.subtle.digest("SHA-256", data);
32034
+ return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join("");
32035
+ }
32036
+ function makeAccessTokenSource(env) {
32037
+ const direct = readEnvVar("GOG_ACCESS_TOKEN", { env });
32038
+ if (direct) return async () => direct;
32039
+ const refreshToken = readEnvVar("GOG_REFRESH_TOKEN", { env });
32040
+ if (!refreshToken) return void 0;
32041
+ const clientId = readEnvVar("GOG_CLIENT_ID", { env });
32042
+ const clientSecret = readEnvVar("GOG_CLIENT_SECRET", { env });
32043
+ if (!clientId || !clientSecret) {
32044
+ const missing = [!clientId && "GOG_CLIENT_ID", !clientSecret && "GOG_CLIENT_SECRET"].filter(Boolean).join(" and ");
32045
+ return async () => {
32046
+ throw new Error(
32047
+ `GOG_REFRESH_TOKEN is set but ${missing} is not, so no access token can be minted. Set the OAuth client alongside the refresh token, or unset GOG_REFRESH_TOKEN to use the backend\u2019s own identity.`
32048
+ );
32049
+ };
32050
+ }
32051
+ return async () => {
32052
+ const key = await cacheKey(refreshToken, clientId);
32053
+ const hit = cache.get(key);
32054
+ if (hit && hit.expiresAt - EXPIRY_MARGIN_MS > Date.now()) return hit.accessToken;
32055
+ let pending = inFlight.get(key);
32056
+ if (!pending) {
32057
+ pending = exchange(refreshToken, clientId, clientSecret).then((minted2) => {
32058
+ cache.set(key, minted2);
32059
+ return minted2;
32060
+ }).finally(() => inFlight.delete(key));
32061
+ inFlight.set(key, pending);
32062
+ }
32063
+ const minted = await pending;
32064
+ return minted.accessToken;
32065
+ };
32066
+ }
32067
+ async function exchange(refreshToken, clientId, clientSecret) {
32068
+ let res;
32069
+ try {
32070
+ res = await fetch(TOKEN_ENDPOINT, {
32071
+ method: "POST",
32072
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
32073
+ body: new URLSearchParams({
32074
+ grant_type: "refresh_token",
32075
+ refresh_token: refreshToken,
32076
+ client_id: clientId,
32077
+ client_secret: clientSecret
32078
+ }).toString()
32079
+ });
32080
+ } catch (err) {
32081
+ throw new Error(
32082
+ `the Google token exchange could not be reached: ${err instanceof Error ? err.message : String(err)}`
32083
+ );
32084
+ }
32085
+ const body = await res.json().catch(() => ({}));
32086
+ if (!res.ok) {
32087
+ if (body.error === "invalid_grant") {
32088
+ throw new Error(
32089
+ 'the stored refresh token has expired or been revoked, so this account must be re-authorized (commonly the 7-day limit on OAuth consent screens still in "Testing" mode). Re-enrol with gog_auth_add_url + gog_auth_add_complete and store the new refresh token.'
32090
+ );
32091
+ }
32092
+ throw new Error(
32093
+ `the access token could not be refreshed (HTTP ${res.status}${body.error ? `, ${body.error}` : ""})`
32094
+ );
32095
+ }
32096
+ if (!body.access_token) {
32097
+ throw new Error("the access token could not be refreshed: Google returned no access_token");
32098
+ }
32099
+ const expiresInMs = (body.expires_in ?? 3600) * 1e3;
32100
+ return { accessToken: body.access_token, expiresAt: Date.now() + expiresInMs };
32101
+ }
32001
32102
 
32002
32103
  // ../gogcli-mcp/src/connector-runtime.ts
32003
32104
  var DEFAULT_TIMEOUT_MS = 3e4;
32004
32105
  var DEADLINE_GRACE_MS = 5e3;
32005
32106
  var RUNNER_GOG_FAILED = 422;
32006
32107
  var RUNNER_DRAINING = 503;
32007
- function makeFlyExecutor(endpoint, key) {
32108
+ function makeFlyExecutor(endpoint, key, readAccessToken) {
32008
32109
  return async (args, opts) => {
32009
32110
  const deadlineMs = (opts?.timeout ?? DEFAULT_TIMEOUT_MS) + DEADLINE_GRACE_MS;
32111
+ const accessToken = await readAccessToken?.();
32010
32112
  let res;
32011
32113
  try {
32012
32114
  res = await fetch(endpoint + "/run", {
@@ -32015,7 +32117,7 @@ function makeFlyExecutor(endpoint, key) {
32015
32117
  Authorization: "Bearer " + key,
32016
32118
  "Content-Type": "application/json"
32017
32119
  },
32018
- body: JSON.stringify({ args }),
32120
+ body: JSON.stringify(accessToken ? { args, accessToken } : { args }),
32019
32121
  signal: AbortSignal.timeout(deadlineMs)
32020
32122
  });
32021
32123
  } catch (err) {
@@ -32056,7 +32158,9 @@ function useRemoteGogRunner(env = process.env) {
32056
32158
  const endpoint = readEnvVar("GOG_RUNNER_URL", { env });
32057
32159
  const key = readEnvVar("GOG_RUNNER_KEY", { env });
32058
32160
  if (!endpoint || !key) return false;
32059
- setDefaultGogExecutor(makeFlyExecutor(endpoint.replace(/\/+$/, ""), key));
32161
+ setDefaultGogExecutor(
32162
+ makeFlyExecutor(endpoint.replace(/\/+$/, ""), key, makeAccessTokenSource(env))
32163
+ );
32060
32164
  return true;
32061
32165
  }
32062
32166
 
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": "2.19.2",
6
+ "version": "2.21.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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gogcli-mcp-drive",
3
- "version": "2.19.2",
3
+ "version": "2.21.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,8 +24,8 @@
24
24
  "test:coverage": "vitest run --coverage"
25
25
  },
26
26
  "dependencies": {
27
- "@chrischall/mcp-utils": "^0.13.3",
28
- "@modelcontextprotocol/sdk": "^1.29.0",
27
+ "@chrischall/mcp-utils": "^0.14.0",
28
+ "@modelcontextprotocol/sdk": "^1.30.0",
29
29
  "zod": "^4.4.3"
30
30
  },
31
31
  "license": "MIT",
package/server.json CHANGED
@@ -7,12 +7,12 @@
7
7
  "source": "github",
8
8
  "subfolder": "packages/gogcli-mcp-drive"
9
9
  },
10
- "version": "2.19.2",
10
+ "version": "2.21.0",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "gogcli-mcp-drive",
15
- "version": "2.19.2",
15
+ "version": "2.21.0",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  },