gogcli-mcp-drive 4.7.0 → 4.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +3 -3
- package/dist/index.js +88 -59
- package/manifest.json +23 -2
- package/package.json +2 -2
- package/server.json +20 -2
- package/src/tools/drive-extra.ts +33 -31
- package/tests/tool-annotations.test.ts +10 -0
|
@@ -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.7.
|
|
10
|
+
"version": "4.7.1"
|
|
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.7.
|
|
18
|
+
"version": "4.7.1",
|
|
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.7.
|
|
4
|
+
"version": "4.7.1",
|
|
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",
|
|
@@ -21,6 +21,6 @@
|
|
|
21
21
|
"comments",
|
|
22
22
|
"shared-drives"
|
|
23
23
|
],
|
|
24
|
-
"skills": "
|
|
25
|
-
"
|
|
24
|
+
"skills": ".",
|
|
25
|
+
"mcpServers": "./.mcp.json"
|
|
26
26
|
}
|
package/dist/index.js
CHANGED
|
@@ -34537,11 +34537,19 @@ var DEFAULT_MAX_REQUEST_BODY_SIZE = 4 * 1024 * 1024;
|
|
|
34537
34537
|
var McpToolError = class extends Error {
|
|
34538
34538
|
/** Actionable remediation text, when one applies. */
|
|
34539
34539
|
hint;
|
|
34540
|
+
/** The upstream HTTP status this failure came from, when known. */
|
|
34541
|
+
status;
|
|
34542
|
+
/** What kind of failure this is, when the thrower knows. */
|
|
34543
|
+
kind;
|
|
34540
34544
|
constructor(message, opts) {
|
|
34541
34545
|
super(message, opts?.cause !== void 0 ? { cause: opts.cause } : void 0);
|
|
34542
34546
|
this.name = "McpToolError";
|
|
34543
34547
|
if (opts?.hint !== void 0)
|
|
34544
34548
|
this.hint = opts.hint;
|
|
34549
|
+
if (opts?.status !== void 0)
|
|
34550
|
+
this.status = opts.status;
|
|
34551
|
+
if (opts?.kind !== void 0)
|
|
34552
|
+
this.kind = opts.kind;
|
|
34545
34553
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
34546
34554
|
}
|
|
34547
34555
|
};
|
|
@@ -34566,7 +34574,7 @@ var API_KEY_RE = new RegExp([
|
|
|
34566
34574
|
// webhook signing secret (Stripe-style)
|
|
34567
34575
|
].map((p) => `\\b${p}`).join("|"), "g");
|
|
34568
34576
|
var GOOGLE_OAUTH_TOKEN_RE = /(?<![A-Za-z0-9+/])(?:ya29\.|1\/\/)[A-Za-z0-9._-]{8,}/g;
|
|
34569
|
-
var QUERY_SECRET_RE = new RegExp(`([?&](?:(?:access|refresh|id|auth|session|csrf|xsrf)[_-]?token|client[_-]?secret|api[_-]?secret|api[_-]?key|password|passwd|signature|token|key|sig|(?:php|j)?sess(?:ion)?[_-]?id|sid|x[-_]${HEADER_NAME_RUN}?(?:api[-_]?key|token|secret|auth)${HEADER_NAME_RUN})=)[^&#\\s"'<>\`]+`, "gi");
|
|
34577
|
+
var QUERY_SECRET_RE = new RegExp(`([?&](?:(?:access|refresh|id|auth|session|csrf|xsrf)[_-]?token|client[_-]?secret|api[_-]?secret|api[_-]?key|password|passwd|signature|token|key|sig|(?:code[_-]?)?verifier|(?:php|j)?sess(?:ion)?[_-]?id|sid|x[-_]${HEADER_NAME_RUN}?(?:api[-_]?key|token|secret|auth)${HEADER_NAME_RUN})=)[^&#\\s"'<>\`]+`, "gi");
|
|
34570
34578
|
var OAUTH_CODE_RE = /([?&]code=)(?=[^&#\s"'<>`]{16,})[^&#\s"'<>`]+/gi;
|
|
34571
34579
|
var FORM_PASSWORD_RE = /((?:^|[\s,;:"'(\[{])(?:password|passwd)=)[^&#\s"'<>`]+/gim;
|
|
34572
34580
|
var HEADER_SECRET_RE = new RegExp(`(\\b(?:x[-_]${HEADER_NAME_RUN}?(?:api[-_]?key|token|secret|auth)${HEADER_NAME_RUN}|api[-_]?key)\\s*:\\s*)[^\\s,;"'<>\`]+`, "gi");
|
|
@@ -34577,6 +34585,8 @@ var JSON_SECRET_KEYS = [
|
|
|
34577
34585
|
"api[_-]?secret",
|
|
34578
34586
|
"(?:x[_-])?api[_-]?key",
|
|
34579
34587
|
"private[_-]?key",
|
|
34588
|
+
"code[_-]?verifier",
|
|
34589
|
+
// PKCE; a bare `verifier` key is too generic for JSON
|
|
34580
34590
|
`x[_-]${HEADER_NAME_RUN}?(?:api[_-]?key|token|secret|auth)${HEADER_NAME_RUN}`,
|
|
34581
34591
|
// bounded like HEADER_SECRET_RE
|
|
34582
34592
|
"(?:proxy-)?authorization",
|
|
@@ -34607,6 +34617,11 @@ function redactSecrets(text) {
|
|
|
34607
34617
|
return text.replace(BEARER_RE, "$1[REDACTED]").replace(BASIC_AUTH_RE, "$1[REDACTED]").replace(SET_COOKIE_RE, "$1$2=[REDACTED]").replace(COOKIE_HEADER_RE, (_m, prefix, pairs) => `${prefix}${pairs.replace(/=[^;,\s]*/g, "=[REDACTED]")}`).replace(API_KEY_RE, "[REDACTED]").replace(GOOGLE_OAUTH_TOKEN_RE, "[REDACTED]").replace(HEADER_SECRET_RE, "$1[REDACTED]").replace(QUERY_SECRET_RE, "$1[REDACTED]").replace(OAUTH_CODE_RE, "$1[REDACTED]").replace(FORM_PASSWORD_RE, "$1[REDACTED]").replace(AWS_SIGV4_RE, "$1[REDACTED]").replace(JSON_COOKIE_DQ_RE, redactJsonCookie('"')).replace(JSON_COOKIE_SQ_RE, redactJsonCookie("'")).replace(JSON_SECRET_DQ_RE, "$1[REDACTED]$2").replace(JSON_SECRET_SQ_RE, "$1[REDACTED]$2").replace(JSON_SECRET_NUM_RE, "$1[REDACTED]").replace(JWT_RE, "[REDACTED]");
|
|
34608
34618
|
}
|
|
34609
34619
|
var ERROR_REDACTION_INPUT_MAX = 64 * 1024;
|
|
34620
|
+
function messageOf(err) {
|
|
34621
|
+
if (err instanceof Error)
|
|
34622
|
+
return err.message;
|
|
34623
|
+
return String(err);
|
|
34624
|
+
}
|
|
34610
34625
|
|
|
34611
34626
|
// ../../node_modules/@chrischall/mcp-utils/dist/response/view.js
|
|
34612
34627
|
var VIEWS = ["compact", "full", "raw"];
|
|
@@ -35048,7 +35063,7 @@ function readEnvVar(key, opts = {}) {
|
|
|
35048
35063
|
if (typeof raw === "string") {
|
|
35049
35064
|
const trimmed = raw.trim();
|
|
35050
35065
|
if (trimmed.length > 0 && trimmed !== "undefined" && trimmed !== "null" && !PLACEHOLDER_RE.test(trimmed)) {
|
|
35051
|
-
return trimmed;
|
|
35066
|
+
return opts.trim === false ? raw : trimmed;
|
|
35052
35067
|
}
|
|
35053
35068
|
}
|
|
35054
35069
|
return opts.default;
|
|
@@ -35243,12 +35258,19 @@ function boundArgs(args) {
|
|
|
35243
35258
|
}
|
|
35244
35259
|
function confirmationFromEnv(options) {
|
|
35245
35260
|
const { tool, account, confirmToken, subject, instruction, args, env = process.env, spent: callerSpent, ...rest } = options;
|
|
35261
|
+
if (args === void 0 || args === null) {
|
|
35262
|
+
throw new TypeError(`confirmationFromEnv: ${tool} passed no args; pass the tool's validated arguments ({} for a tool with none).`);
|
|
35263
|
+
}
|
|
35246
35264
|
const spent2 = callerSpent ?? spentTokenStoreFromEnv(env);
|
|
35247
35265
|
const mode = readConfirmMode(env);
|
|
35248
35266
|
const ttlSeconds = readConfirmTtl(env);
|
|
35249
35267
|
const elicitationOff = readConfirmElicitation(env) === "off";
|
|
35250
|
-
const bound =
|
|
35251
|
-
const
|
|
35268
|
+
const bound = boundArgs(args);
|
|
35269
|
+
const elicitationBound = account === void 0 ? { args: bound } : { account, args: bound };
|
|
35270
|
+
const bindingAdded = rest.binding ? rest : {
|
|
35271
|
+
...rest,
|
|
35272
|
+
binding: { key: confirmKeyFromEnv(env), args: elicitationBound, ttlSeconds: ttlSeconds ?? DEFAULT_TTL_SECONDS2 }
|
|
35273
|
+
};
|
|
35252
35274
|
const confirmation = elicitationOff ? { ...bindingAdded, elicitation: false } : bindingAdded;
|
|
35253
35275
|
if (mode === "refuse" || ttlSeconds === void 0) {
|
|
35254
35276
|
const reason = mode === "refuse" ? REFUSE_HINT : BAD_TTL_HINT;
|
|
@@ -35258,7 +35280,7 @@ function confirmationFromEnv(options) {
|
|
|
35258
35280
|
unsupportedNote: confirmation.unsupportedNote ? `${confirmation.unsupportedNote} ${hint}` : hint
|
|
35259
35281
|
};
|
|
35260
35282
|
}
|
|
35261
|
-
const boundSubject =
|
|
35283
|
+
const boundSubject = async () => {
|
|
35262
35284
|
const read = await subject();
|
|
35263
35285
|
if ("content" in read)
|
|
35264
35286
|
return read;
|
|
@@ -35285,12 +35307,14 @@ var SERVER_PROTOCOL_VERSIONS = Object.freeze([
|
|
|
35285
35307
|
...SUPPORTED_PROTOCOL_VERSIONS
|
|
35286
35308
|
]);
|
|
35287
35309
|
function hintResultOrRethrow(err) {
|
|
35310
|
+
if (err instanceof ProtocolError && err.code === ProtocolErrorCode.UrlElicitationRequired)
|
|
35311
|
+
throw err;
|
|
35288
35312
|
if (err instanceof McpToolError && err.hint) {
|
|
35289
35313
|
return errorResult(`${err.message}
|
|
35290
35314
|
|
|
35291
35315
|
Hint: ${err.hint}`);
|
|
35292
35316
|
}
|
|
35293
|
-
|
|
35317
|
+
return errorResult(messageOf(err));
|
|
35294
35318
|
}
|
|
35295
35319
|
function mcpRequestFrom(args) {
|
|
35296
35320
|
const ctx = args.at(-1);
|
|
@@ -35524,7 +35548,7 @@ var PositiveInt = external_exports.number().int().positive();
|
|
|
35524
35548
|
var NonNegInt = external_exports.number().int().nonnegative();
|
|
35525
35549
|
var NonEmptyString = external_exports.string().min(1);
|
|
35526
35550
|
var IsoDate = external_exports.iso.date();
|
|
35527
|
-
var IsoTime = external_exports.string().regex(/^([01]?\d|2[0-3]):[0-5]\d$/, "must be HH:MM (24h), e.g. 19:30");
|
|
35551
|
+
var IsoTime = external_exports.string().regex(/^([01]?\d|2[0-3]):[0-5]\d$/, "must be HH:MM (24h), e.g. 19:30").overwrite((v) => v.padStart(5, "0"));
|
|
35528
35552
|
var NumericIdString = external_exports.string().regex(/^\d+$/, "must be a numeric id (digits only)").describe("A numeric id string (digits only), safe to interpolate into a URL path.");
|
|
35529
35553
|
var SafePathSegment = external_exports.string().min(1).regex(/^[^/?#\s]+$/, 'must not contain "/", "?", "#", or whitespace').refine((v) => !v.includes(".."), 'must not contain ".."').describe('A single URL path segment, safe to interpolate: no "/", "..", "?", "#", or whitespace.');
|
|
35530
35554
|
var schemaOrigin = external_exports.string().optional().describe("Portal origin (e.g. https://<vendor>.example.co) selecting which active session to use. Optional when only one session is active.");
|
|
@@ -36222,7 +36246,7 @@ function registerRunTool(server, options) {
|
|
|
36222
36246
|
}
|
|
36223
36247
|
server.registerTool(`gog_${service}_run`, {
|
|
36224
36248
|
description,
|
|
36225
|
-
annotations: { destructiveHint: true },
|
|
36249
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
36226
36250
|
inputSchema: external_exports.object(inputSchema)
|
|
36227
36251
|
}, async (rawArgs) => {
|
|
36228
36252
|
const { subcommand, args, account } = rawArgs;
|
|
@@ -36439,6 +36463,7 @@ async function requireDispatchConfirmation(ctx, options) {
|
|
|
36439
36463
|
...confirmation,
|
|
36440
36464
|
tool: fallback.tool,
|
|
36441
36465
|
account: fallback.account ?? readEnvVar("GOG_ACCOUNT") ?? "",
|
|
36466
|
+
args: fallback.args,
|
|
36442
36467
|
confirmToken: fallback.confirmToken,
|
|
36443
36468
|
subject: fallback.subject,
|
|
36444
36469
|
instruction: fallback.instruction ?? CONFIRM_ACTION_INSTRUCTION,
|
|
@@ -36451,13 +36476,13 @@ function resultText(result) {
|
|
|
36451
36476
|
}
|
|
36452
36477
|
|
|
36453
36478
|
// ../gogcli-mcp/src/tools/auth.ts
|
|
36454
|
-
var AUTH_RUN_SUBCOMMANDS = ["list", "status", "services", "
|
|
36479
|
+
var AUTH_RUN_SUBCOMMANDS = ["list", "status", "services", "alias"];
|
|
36455
36480
|
function registerAuthToolsWith(server, defaultServices) {
|
|
36456
36481
|
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.`;
|
|
36457
36482
|
const extraScopesDescribe = "Additional raw OAuth scope URIs to request, comma-separated, on top of the ones `services` implies. Use for scopes no service covers \u2014 e.g. https://www.googleapis.com/auth/bigquery.readonly, required before gog_sheets_datasource_* can read BigQuery-backed Connected Sheets. Leave unset otherwise: an extra scope whose API is not enabled on the OAuth client project makes Google reject the WHOLE authorization with invalid_scope.";
|
|
36458
36483
|
server.registerTool("gog_auth_list", {
|
|
36459
36484
|
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.",
|
|
36460
|
-
annotations: { readOnlyHint: true },
|
|
36485
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
36461
36486
|
inputSchema: external_exports.object({})
|
|
36462
36487
|
}, async () => {
|
|
36463
36488
|
try {
|
|
@@ -36468,7 +36493,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
36468
36493
|
});
|
|
36469
36494
|
server.registerTool("gog_auth_status", {
|
|
36470
36495
|
description: "Show gogcli auth CONFIGURATION: keyring backend, credential files, and auth setup. Despite the name this is not a health check \u2014 it reads local setup and does not contact Google, so it says nothing about whether an account can still authenticate. Use gog_auth_health for that.",
|
|
36471
|
-
annotations: { readOnlyHint: true },
|
|
36496
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
36472
36497
|
inputSchema: external_exports.object({})
|
|
36473
36498
|
}, async () => {
|
|
36474
36499
|
try {
|
|
@@ -36479,7 +36504,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
36479
36504
|
});
|
|
36480
36505
|
server.registerTool("gog_auth_health", {
|
|
36481
36506
|
description: `Check the LIVE health of each stored Google account. Unlike gog_auth_status (which only reports keyring/config setup), this performs a real token refresh against Google, so it detects expired or revoked (invalid_grant) refresh tokens \u2014 the account-wide sign-out that blocks every service. Reports per account: whether the token is currently valid, the mapped cause when it is not, how long ago it was authorized, and a warning as it approaches the 7-day refresh-token limit that applies to OAuth apps whose consent screen is still in "Testing" mode. Run it proactively to re-authorize on your own schedule instead of mid-task. On a hosted deployment this is the ONLY check that measures Google: a connector showing "connected" or "refreshed" has verified the client's connection to the MCP host, and nothing else \u2014 the Google credential lives in gog's keyring on that host and can be dead while the connection looks perfectly healthy.`,
|
|
36482
|
-
annotations: { readOnlyHint: true },
|
|
36507
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
36483
36508
|
inputSchema: external_exports.object({})
|
|
36484
36509
|
}, async () => {
|
|
36485
36510
|
try {
|
|
@@ -36490,7 +36515,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
36490
36515
|
});
|
|
36491
36516
|
server.registerTool("gog_auth_services", {
|
|
36492
36517
|
description: "List all Google services supported by gogcli and the OAuth scopes each requires.",
|
|
36493
|
-
annotations: { readOnlyHint: true },
|
|
36518
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
36494
36519
|
inputSchema: external_exports.object({})
|
|
36495
36520
|
}, async () => {
|
|
36496
36521
|
try {
|
|
@@ -36501,7 +36526,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
36501
36526
|
});
|
|
36502
36527
|
server.registerTool("gog_auth_add", {
|
|
36503
36528
|
description: "Authorize a Google account via browser-based OAuth. Opens a browser window where the user must sign in and grant access. Blocks for up to 5 minutes waiting for the user to complete authorization. If the browser does not open automatically, a fallback URL is included in the response. Use gog_auth_list to check which accounts are already configured.",
|
|
36504
|
-
annotations: { destructiveHint: true },
|
|
36529
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
36505
36530
|
inputSchema: external_exports.object({
|
|
36506
36531
|
email: external_exports.string().describe("Google account email to authorize"),
|
|
36507
36532
|
services: external_exports.string().optional().default(defaultServices).describe(servicesDescribe),
|
|
@@ -36521,7 +36546,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
36521
36546
|
});
|
|
36522
36547
|
server.registerTool("gog_auth_add_url", {
|
|
36523
36548
|
description: "Begin REMOTE/headless Google authorization (step 1 of 2). Returns a sign-in URL to open in any browser \u2014 no local server or terminal on the gogcli host is needed, so this works on a hosted deployment where the interactive gog_auth_add cannot. Hand the URL to the user; after they sign in, the browser is redirected to a localhost URL that fails to load \u2014 that is expected. They copy that full redirected URL (from the address bar) and you pass it to gog_auth_add_complete. The link is valid for 10 minutes. If you pass a custom `services` here, pass the SAME value to gog_auth_add_complete or the second step will not match this one.",
|
|
36524
|
-
annotations: { readOnlyHint: true },
|
|
36549
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
36525
36550
|
inputSchema: external_exports.object({
|
|
36526
36551
|
email: external_exports.string().describe("Google account email to authorize"),
|
|
36527
36552
|
services: external_exports.string().optional().default(defaultServices).describe(servicesDescribe),
|
|
@@ -36538,7 +36563,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
36538
36563
|
});
|
|
36539
36564
|
server.registerTool("gog_auth_add_complete", {
|
|
36540
36565
|
description: "Complete REMOTE/headless Google authorization (step 2 of 2). Takes the full redirected localhost URL the user copied after finishing gog_auth_add_url, exchanges it for a refresh token, and stores it. Use the SAME `services` value you passed to gog_auth_add_url. Must run within 10 minutes of step 1 and against the same gogcli host.",
|
|
36541
|
-
annotations: { destructiveHint: true },
|
|
36566
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
36542
36567
|
inputSchema: external_exports.object({
|
|
36543
36568
|
email: external_exports.string().describe("Google account email being authorized (same as step 1)"),
|
|
36544
36569
|
redirectUrl: external_exports.string().describe(
|
|
@@ -36574,7 +36599,7 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
36574
36599
|
});
|
|
36575
36600
|
registerRunTool(server, {
|
|
36576
36601
|
service: "auth",
|
|
36577
|
-
examples: '"
|
|
36602
|
+
examples: '"alias", "list", "status"',
|
|
36578
36603
|
omitAccount: true,
|
|
36579
36604
|
allowedSubcommands: AUTH_RUN_SUBCOMMANDS,
|
|
36580
36605
|
note: "For browser-based authorization, use gog_auth_add instead."
|
|
@@ -36731,7 +36756,7 @@ var DRIVE_LS_COMPACT_FIELDS = "nextPageToken,files(id,name,mimeType,modifiedTime
|
|
|
36731
36756
|
function registerDriveTools(server) {
|
|
36732
36757
|
server.registerTool("gog_drive_ls", {
|
|
36733
36758
|
description: "List files in a Google Drive folder (default: root).",
|
|
36734
|
-
annotations: { readOnlyHint: true },
|
|
36759
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
36735
36760
|
inputSchema: external_exports.object({
|
|
36736
36761
|
folderId: external_exports.string().optional().describe("Folder ID to list (default: root)"),
|
|
36737
36762
|
max: external_exports.number().optional().describe("Max results (default: 20)"),
|
|
@@ -36760,7 +36785,7 @@ function registerDriveTools(server) {
|
|
|
36760
36785
|
});
|
|
36761
36786
|
server.registerTool("gog_drive_search", {
|
|
36762
36787
|
description: "Search Google Drive files by full-text query.",
|
|
36763
|
-
annotations: { readOnlyHint: true },
|
|
36788
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
36764
36789
|
inputSchema: external_exports.object({
|
|
36765
36790
|
query: external_exports.string().describe("Search query"),
|
|
36766
36791
|
// gog's `drive search` accepts no --fields mask, so unlike gog_drive_ls
|
|
@@ -36777,7 +36802,7 @@ function registerDriveTools(server) {
|
|
|
36777
36802
|
});
|
|
36778
36803
|
server.registerTool("gog_drive_get", {
|
|
36779
36804
|
description: "Get metadata for a Google Drive file.",
|
|
36780
|
-
annotations: { readOnlyHint: true },
|
|
36805
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
36781
36806
|
inputSchema: external_exports.object({
|
|
36782
36807
|
fileId: external_exports.string().describe("File ID"),
|
|
36783
36808
|
// A --fields mask saves only 7% here: the default set is already narrow,
|
|
@@ -36797,7 +36822,7 @@ function registerDriveTools(server) {
|
|
|
36797
36822
|
});
|
|
36798
36823
|
server.registerTool("gog_drive_mkdir", {
|
|
36799
36824
|
description: "Create a new folder in Google Drive.",
|
|
36800
|
-
annotations: { destructiveHint: false },
|
|
36825
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
36801
36826
|
inputSchema: external_exports.object({
|
|
36802
36827
|
name: external_exports.string().describe("Folder name"),
|
|
36803
36828
|
account: accountParam
|
|
@@ -36807,7 +36832,7 @@ function registerDriveTools(server) {
|
|
|
36807
36832
|
});
|
|
36808
36833
|
server.registerTool("gog_drive_rename", {
|
|
36809
36834
|
description: "Rename a file or folder in Google Drive.",
|
|
36810
|
-
annotations: { destructiveHint: false },
|
|
36835
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
36811
36836
|
inputSchema: external_exports.object({
|
|
36812
36837
|
fileId: external_exports.string().describe("File or folder ID"),
|
|
36813
36838
|
newName: external_exports.string().describe("New name"),
|
|
@@ -36818,7 +36843,7 @@ function registerDriveTools(server) {
|
|
|
36818
36843
|
});
|
|
36819
36844
|
server.registerTool("gog_drive_move", {
|
|
36820
36845
|
description: "Move a file to a different folder in Google Drive.",
|
|
36821
|
-
annotations: { destructiveHint: false },
|
|
36846
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
36822
36847
|
inputSchema: external_exports.object({
|
|
36823
36848
|
fileId: external_exports.string().describe("File ID to move"),
|
|
36824
36849
|
parentId: external_exports.string().describe("Destination folder ID"),
|
|
@@ -36829,7 +36854,7 @@ function registerDriveTools(server) {
|
|
|
36829
36854
|
});
|
|
36830
36855
|
server.registerTool("gog_drive_delete", {
|
|
36831
36856
|
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,
|
|
36832
|
-
annotations: { destructiveHint: true },
|
|
36857
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
36833
36858
|
inputSchema: external_exports.object({
|
|
36834
36859
|
fileId: external_exports.string().describe("File ID to delete"),
|
|
36835
36860
|
permanent: external_exports.boolean().optional().describe("Permanently delete instead of moving to trash (irreversible; asks the user first)"),
|
|
@@ -36849,6 +36874,7 @@ function registerDriveTools(server) {
|
|
|
36849
36874
|
details: target,
|
|
36850
36875
|
unsupportedNote: "Move it to the trash instead (permanent=false); the user can empty the trash from Google Drive.",
|
|
36851
36876
|
fallback: {
|
|
36877
|
+
args: { fileId, permanent, account },
|
|
36852
36878
|
tool: "gog_drive_delete",
|
|
36853
36879
|
account,
|
|
36854
36880
|
confirmToken,
|
|
@@ -36863,7 +36889,7 @@ function registerDriveTools(server) {
|
|
|
36863
36889
|
});
|
|
36864
36890
|
server.registerTool("gog_drive_share", {
|
|
36865
36891
|
description: "Share a Google Drive file or folder. Granting access is the risk even though no email is sent (gog does not notify by default), so this reads the file and asks the MCP host to show the user a confirmation prompt naming it, who gets access and with what role; nothing is shared unless they accept." + CONFIRM_FALLBACK_DESCRIPTION,
|
|
36866
|
-
annotations: { destructiveHint: true },
|
|
36892
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
36867
36893
|
inputSchema: external_exports.object({
|
|
36868
36894
|
fileId: external_exports.string().describe("File or folder ID"),
|
|
36869
36895
|
to: external_exports.enum(["user", "anyone", "domain"]).describe("Share target type"),
|
|
@@ -36892,6 +36918,7 @@ function registerDriveTools(server) {
|
|
|
36892
36918
|
details: grant,
|
|
36893
36919
|
unsupportedNote: "Ask the user to share it themselves from Google Drive.",
|
|
36894
36920
|
fallback: {
|
|
36921
|
+
args: { fileId, to, email: email3, domain: domain2, role, account },
|
|
36895
36922
|
tool: "gog_drive_share",
|
|
36896
36923
|
account,
|
|
36897
36924
|
confirmToken,
|
|
@@ -36909,7 +36936,7 @@ function registerDriveTools(server) {
|
|
|
36909
36936
|
server.registerTool("gog_drive_extract_text", {
|
|
36910
36937
|
description: `Extract readable TEXT from a Drive file \u2014 PDF, image, docx, or a native Google Doc \u2014 and return it to you directly (closing the webViewLink dead end). Accepts a Drive file id, including the id from gog_gmail_attachment's deliveredVia:"drive" response. For non-Docs it copies the file to a temporary Google Doc so Drive OCRs it (works for scanned PDFs too), exports plain text, then deletes the temp Doc. Operates entirely via the Drive API within the existing drive scope \u2014 no host filesystem, no scope widening. For a large file, page through with offset/maxChars.`,
|
|
36911
36938
|
// Creates and deletes a temporary Doc for non-native files, so not read-only.
|
|
36912
|
-
annotations: { destructiveHint: false },
|
|
36939
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
36913
36940
|
inputSchema: external_exports.object({
|
|
36914
36941
|
fileId: external_exports.string().describe("Drive file ID (e.g. the id returned by gog_gmail_attachment)"),
|
|
36915
36942
|
ocrLanguage: external_exports.string().optional().describe(
|
|
@@ -36974,7 +37001,7 @@ function registerDriveTools(server) {
|
|
|
36974
37001
|
});
|
|
36975
37002
|
server.registerTool("gog_drive_read_bytes", {
|
|
36976
37003
|
description: "Fetch a Drive file's raw bytes and return them base64-encoded as an embedded resource \u2014 the generic fallback for callers that want the file itself (to parse locally) rather than extracted text. Files over 8 MiB are refused (checked before downloading). For readable text from a PDF, prefer gog_drive_extract_text.",
|
|
36977
|
-
annotations: { readOnlyHint: true },
|
|
37004
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
36978
37005
|
inputSchema: external_exports.object({
|
|
36979
37006
|
fileId: external_exports.string().describe("Drive file ID"),
|
|
36980
37007
|
account: accountParam
|
|
@@ -37047,7 +37074,7 @@ var failIfNotEmptyParam = external_exports.boolean().optional().describe(
|
|
|
37047
37074
|
);
|
|
37048
37075
|
|
|
37049
37076
|
// ../gogcli-mcp/src/server.ts
|
|
37050
|
-
var VERSION = true ? "4.7.
|
|
37077
|
+
var VERSION = true ? "4.7.1" : "0.0.0";
|
|
37051
37078
|
|
|
37052
37079
|
// ../gogcli-mcp/src/bootstrap-auth.ts
|
|
37053
37080
|
import { join as join7 } from "node:path";
|
|
@@ -37126,7 +37153,7 @@ function registerExtraDriveTools(server) {
|
|
|
37126
37153
|
server.registerTool("gog_drive_download", {
|
|
37127
37154
|
description: "Download a Drive file to the local filesystem. For Google Docs formats, specify an export format (pdf, csv, xlsx, pptx, txt, png, docx, md).",
|
|
37128
37155
|
// Writes a file on the gog host and can overwrite one: not read-only (audit SEC-4).
|
|
37129
|
-
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
37156
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
37130
37157
|
inputSchema: external_exports.object({
|
|
37131
37158
|
fileId: external_exports.string().describe("File ID to download"),
|
|
37132
37159
|
out: external_exports.string().optional().describe("Output file path (default: gogcli config dir)"),
|
|
@@ -37144,7 +37171,7 @@ function registerExtraDriveTools(server) {
|
|
|
37144
37171
|
});
|
|
37145
37172
|
server.registerTool("gog_drive_upload", {
|
|
37146
37173
|
description: `Upload a file to Drive. Supply it EITHER as localPath (a path on the gog server's own filesystem \u2014 local stdio deployments only) OR as content (the bytes, base64-encoded, carried with the request), which is what works when this server runs remotely from you and no path you can name exists on it. Use replace to replace the content of an existing file (preserves link/permissions), or convert to auto-convert to a Google format. Pair replace with ifVersion to make the overwrite conditional: gog sends an atomic If-Match precondition and reports a conflict \u2014 applying nothing \u2014 if the file changed since you read it, instead of silently clobbering a concurrent edit. No read tool surfaces the version number: fetch it immediately before uploading with gog_drive_run { subcommand: "raw", args: ["<fileId>", "--fields=version"] } \u2014 it comes back as a JSON string, so pass it on as a number. Conditional replacement refuses Google Workspace files (Docs/Sheets/Slides), which have no replaceable binary content.`,
|
|
37147
|
-
annotations: { destructiveHint: false },
|
|
37174
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
37148
37175
|
inputSchema: external_exports.object({
|
|
37149
37176
|
localPath: external_exports.string().optional().describe(`Path to the file to upload, resolved ON THE GOG SERVER's filesystem \u2014 NOT this client's. Only usable when gog runs on the same machine you do (local stdio); on a hosted deployment (e.g. mcp-host) this path does not exist and the call fails with "no such file or directory" \u2014 use content there. Exactly one of localPath / content is required. Must be inside the server's GOG_FILE_ROOTS directories (default ~/gogcli-mcp-files).`),
|
|
37150
37177
|
content: external_exports.string().optional().describe("The file's bytes, base64-encoded (standard alphabet, with padding) \u2014 upload a file you hold without it existing anywhere on the gog server. This is the only route that works when the caller and gog share no filesystem. Requires name (there is no path to take a filename from). Max 8 MiB; for anything larger use localPath from a local deployment. Exactly one of localPath / content is required \u2014 supplying both is an error, not a precedence rule."),
|
|
@@ -37195,7 +37222,7 @@ function registerExtraDriveTools(server) {
|
|
|
37195
37222
|
});
|
|
37196
37223
|
server.registerTool("gog_drive_sync_push", {
|
|
37197
37224
|
description: "Recursively push a local directory's contents into an existing Drive folder \u2014 uploads new files, updates changed ones (matched by name + MD5), and creates missing subfolders. One-way and additive: it never deletes anything on Drive. Use dryRun to preview the planned actions first.",
|
|
37198
|
-
annotations: { destructiveHint: true },
|
|
37225
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
37199
37226
|
inputSchema: external_exports.object({
|
|
37200
37227
|
localPath: external_exports.string().describe("Local directory to push (its contents are mirrored into the parent folder). Must be inside the server's GOG_FILE_ROOTS directories (default ~/gogcli-mcp-files)."),
|
|
37201
37228
|
parent: external_exports.string().describe("Existing destination Drive folder ID"),
|
|
@@ -37212,7 +37239,7 @@ function registerExtraDriveTools(server) {
|
|
|
37212
37239
|
});
|
|
37213
37240
|
server.registerTool("gog_drive_copy", {
|
|
37214
37241
|
description: "Copy a Drive file to a new file with the given name.",
|
|
37215
|
-
annotations: { destructiveHint: false },
|
|
37242
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
37216
37243
|
inputSchema: external_exports.object({
|
|
37217
37244
|
fileId: external_exports.string().describe("File ID to copy"),
|
|
37218
37245
|
name: external_exports.string().describe("Name for the new copy"),
|
|
@@ -37226,7 +37253,7 @@ function registerExtraDriveTools(server) {
|
|
|
37226
37253
|
});
|
|
37227
37254
|
server.registerTool("gog_drive_url", {
|
|
37228
37255
|
description: "Print shareable web URLs for one or more Drive files.",
|
|
37229
|
-
annotations: { readOnlyHint: true },
|
|
37256
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37230
37257
|
inputSchema: external_exports.object({
|
|
37231
37258
|
fileIds: external_exports.array(external_exports.string()).min(1).describe("One or more file IDs to get URLs for"),
|
|
37232
37259
|
account: accountParam
|
|
@@ -37236,7 +37263,7 @@ function registerExtraDriveTools(server) {
|
|
|
37236
37263
|
});
|
|
37237
37264
|
server.registerTool("gog_drive_permissions", {
|
|
37238
37265
|
description: "List permissions on a Drive file.",
|
|
37239
|
-
annotations: { readOnlyHint: true },
|
|
37266
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37240
37267
|
inputSchema: external_exports.object({
|
|
37241
37268
|
fileId: external_exports.string().describe("File ID"),
|
|
37242
37269
|
max: external_exports.number().optional().describe("Max results (default: 100)"),
|
|
@@ -37253,7 +37280,7 @@ function registerExtraDriveTools(server) {
|
|
|
37253
37280
|
});
|
|
37254
37281
|
server.registerTool("gog_drive_unshare", {
|
|
37255
37282
|
description: "Remove a permission from a Drive file. Get the permissionId from gog_drive_permissions.",
|
|
37256
|
-
annotations: { destructiveHint: true },
|
|
37283
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
37257
37284
|
inputSchema: external_exports.object({
|
|
37258
37285
|
fileId: external_exports.string().describe("File ID"),
|
|
37259
37286
|
permissionId: external_exports.string().describe("Permission ID to remove"),
|
|
@@ -37264,7 +37291,7 @@ function registerExtraDriveTools(server) {
|
|
|
37264
37291
|
});
|
|
37265
37292
|
server.registerTool("gog_drive_drives_list", {
|
|
37266
37293
|
description: "List shared drives (Team Drives) accessible to the account.",
|
|
37267
|
-
annotations: { readOnlyHint: true },
|
|
37294
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37268
37295
|
inputSchema: external_exports.object({
|
|
37269
37296
|
max: external_exports.number().optional().describe("Max results (default: 100, max allowed: 100)"),
|
|
37270
37297
|
pageToken: pageTokenParam,
|
|
@@ -37284,7 +37311,7 @@ function registerExtraDriveTools(server) {
|
|
|
37284
37311
|
});
|
|
37285
37312
|
server.registerTool("gog_drive_comments_list", {
|
|
37286
37313
|
description: "List comments on a Drive file.",
|
|
37287
|
-
annotations: { readOnlyHint: true },
|
|
37314
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37288
37315
|
inputSchema: external_exports.object({
|
|
37289
37316
|
fileId: external_exports.string().describe("File ID"),
|
|
37290
37317
|
since: external_exports.string().optional().describe("Only return comments modified at or after this RFC3339 timestamp (e.g. 2026-06-01T00:00:00Z)"),
|
|
@@ -37301,7 +37328,7 @@ function registerExtraDriveTools(server) {
|
|
|
37301
37328
|
});
|
|
37302
37329
|
server.registerTool("gog_drive_comments_get", {
|
|
37303
37330
|
description: "Get a single comment on a Drive file by ID.",
|
|
37304
|
-
annotations: { readOnlyHint: true },
|
|
37331
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37305
37332
|
inputSchema: external_exports.object({
|
|
37306
37333
|
fileId: external_exports.string().describe("File ID"),
|
|
37307
37334
|
commentId: external_exports.string().describe("Comment ID"),
|
|
@@ -37312,7 +37339,7 @@ function registerExtraDriveTools(server) {
|
|
|
37312
37339
|
});
|
|
37313
37340
|
server.registerTool("gog_drive_comments_add", {
|
|
37314
37341
|
description: "Add a new comment to a Drive file. Drive notifies the file's owner and everyone the text +mentions, so this reads the file and asks the MCP host to show the user a confirmation prompt with the file, the text and who is mentioned first; nothing is posted unless they accept." + CONFIRM_FALLBACK_DESCRIPTION,
|
|
37315
|
-
annotations: { destructiveHint: false },
|
|
37342
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
37316
37343
|
inputSchema: external_exports.object({
|
|
37317
37344
|
fileId: external_exports.string().describe("File ID"),
|
|
37318
37345
|
content: external_exports.string().describe("Comment text"),
|
|
@@ -37332,6 +37359,7 @@ function registerExtraDriveTools(server) {
|
|
|
37332
37359
|
details: { file: file2, textPreview: bodyPreview(content), mentions },
|
|
37333
37360
|
unsupportedNote: "Ask the user to comment from Google Drive.",
|
|
37334
37361
|
fallback: {
|
|
37362
|
+
args: { fileId, content, account },
|
|
37335
37363
|
tool: "gog_drive_comments_add",
|
|
37336
37364
|
account,
|
|
37337
37365
|
confirmToken,
|
|
@@ -37346,7 +37374,7 @@ function registerExtraDriveTools(server) {
|
|
|
37346
37374
|
});
|
|
37347
37375
|
server.registerTool("gog_drive_comments_update", {
|
|
37348
37376
|
description: "Update the text of an existing comment.",
|
|
37349
|
-
annotations: { destructiveHint: false },
|
|
37377
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
37350
37378
|
inputSchema: external_exports.object({
|
|
37351
37379
|
fileId: external_exports.string().describe("File ID"),
|
|
37352
37380
|
commentId: external_exports.string().describe("Comment ID to update"),
|
|
@@ -37358,7 +37386,7 @@ function registerExtraDriveTools(server) {
|
|
|
37358
37386
|
});
|
|
37359
37387
|
server.registerTool("gog_drive_comments_delete", {
|
|
37360
37388
|
description: "Delete a comment from a Drive file.",
|
|
37361
|
-
annotations: { destructiveHint: true },
|
|
37389
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
37362
37390
|
inputSchema: external_exports.object({
|
|
37363
37391
|
fileId: external_exports.string().describe("File ID"),
|
|
37364
37392
|
commentId: external_exports.string().describe("Comment ID to delete"),
|
|
@@ -37369,7 +37397,7 @@ function registerExtraDriveTools(server) {
|
|
|
37369
37397
|
});
|
|
37370
37398
|
server.registerTool("gog_drive_comments_reply", {
|
|
37371
37399
|
description: 'Reply to an existing comment on a Drive file. Pass `action: "resolve"` or `"reopen"` to atomically flip the parent comment\'s resolved state via the Drive API\'s Reply.action field \u2014 avoids the older workaround of deleting the comment (which destroys review-thread context). Everyone on the thread and anyone the reply +mentions is notified, so this reads the file and the comment and asks the MCP host to show the user a confirmation prompt with both and the reply first; nothing is posted unless they accept.' + CONFIRM_FALLBACK_DESCRIPTION,
|
|
37372
|
-
annotations: { destructiveHint: false },
|
|
37400
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
37373
37401
|
inputSchema: external_exports.object({
|
|
37374
37402
|
fileId: external_exports.string().describe("File ID"),
|
|
37375
37403
|
commentId: external_exports.string().describe("Comment ID to reply to"),
|
|
@@ -37396,6 +37424,7 @@ function registerExtraDriveTools(server) {
|
|
|
37396
37424
|
details: { file: file2, replyingTo, textPreview: bodyPreview(content), mentions, ...action ? { action } : {} },
|
|
37397
37425
|
unsupportedNote: "Ask the user to reply from Google Drive.",
|
|
37398
37426
|
fallback: {
|
|
37427
|
+
args: { fileId, commentId, content, action, account },
|
|
37399
37428
|
tool: "gog_drive_comments_reply",
|
|
37400
37429
|
account,
|
|
37401
37430
|
confirmToken,
|
|
@@ -37410,7 +37439,7 @@ function registerExtraDriveTools(server) {
|
|
|
37410
37439
|
});
|
|
37411
37440
|
server.registerTool("gog_drive_comments_resolve", {
|
|
37412
37441
|
description: 'Resolve a comment on a Drive file (mark as done) without posting a reply. To resolve while replying, use gog_drive_comments_reply with action: "resolve".',
|
|
37413
|
-
annotations: { destructiveHint: true },
|
|
37442
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
37414
37443
|
inputSchema: external_exports.object({
|
|
37415
37444
|
fileId: external_exports.string().describe("File ID"),
|
|
37416
37445
|
commentId: external_exports.string().describe("Comment ID to resolve"),
|
|
@@ -37421,7 +37450,7 @@ function registerExtraDriveTools(server) {
|
|
|
37421
37450
|
});
|
|
37422
37451
|
server.registerTool("gog_drive_comments_reopen", {
|
|
37423
37452
|
description: 'Reopen a previously resolved comment on a Drive file. To reopen while replying, use gog_drive_comments_reply with action: "reopen".',
|
|
37424
|
-
annotations: { destructiveHint: true },
|
|
37453
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
37425
37454
|
inputSchema: external_exports.object({
|
|
37426
37455
|
fileId: external_exports.string().describe("File ID"),
|
|
37427
37456
|
commentId: external_exports.string().describe("Comment ID to reopen"),
|
|
@@ -37432,7 +37461,7 @@ function registerExtraDriveTools(server) {
|
|
|
37432
37461
|
});
|
|
37433
37462
|
server.registerTool("gog_drive_du", {
|
|
37434
37463
|
description: "Summarize Drive folder sizes (disk-usage style) starting from a folder.",
|
|
37435
|
-
annotations: { readOnlyHint: true },
|
|
37464
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37436
37465
|
inputSchema: external_exports.object({
|
|
37437
37466
|
parent: external_exports.string().optional().describe("Folder ID to start from (default: root)"),
|
|
37438
37467
|
depth: external_exports.number().optional().describe("Depth for folder totals (default: 1)"),
|
|
@@ -37454,7 +37483,7 @@ function registerExtraDriveTools(server) {
|
|
|
37454
37483
|
});
|
|
37455
37484
|
server.registerTool("gog_drive_tree", {
|
|
37456
37485
|
description: "Print a read-only folder tree starting from a folder.",
|
|
37457
|
-
annotations: { readOnlyHint: true },
|
|
37486
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37458
37487
|
inputSchema: external_exports.object({
|
|
37459
37488
|
parent: external_exports.string().optional().describe("Folder ID to start from (default: root)"),
|
|
37460
37489
|
depth: external_exports.number().optional().describe("Max depth (0 = unlimited; default: 2)"),
|
|
@@ -37472,7 +37501,7 @@ function registerExtraDriveTools(server) {
|
|
|
37472
37501
|
});
|
|
37473
37502
|
server.registerTool("gog_drive_changes_start_token", {
|
|
37474
37503
|
description: "Get a Drive changes start page token \u2014 the cursor you pass to gog_drive_changes_list to enumerate changes since this point.",
|
|
37475
|
-
annotations: { readOnlyHint: true },
|
|
37504
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37476
37505
|
inputSchema: external_exports.object({
|
|
37477
37506
|
drive: external_exports.string().optional().describe("Shared drive ID for a shared-drive change log"),
|
|
37478
37507
|
account: accountParam
|
|
@@ -37484,7 +37513,7 @@ function registerExtraDriveTools(server) {
|
|
|
37484
37513
|
});
|
|
37485
37514
|
server.registerTool("gog_drive_changes_list", {
|
|
37486
37515
|
description: "List Drive changes since a page token (for sync/automation). Get the initial token from gog_drive_changes_start_token; the response includes a newStartPageToken to persist for the next poll.",
|
|
37487
|
-
annotations: { readOnlyHint: true },
|
|
37516
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37488
37517
|
inputSchema: external_exports.object({
|
|
37489
37518
|
token: external_exports.string().describe("Start page token or next page token"),
|
|
37490
37519
|
max: external_exports.number().optional().describe("Max results (default: 100)"),
|
|
@@ -37507,7 +37536,7 @@ function registerExtraDriveTools(server) {
|
|
|
37507
37536
|
});
|
|
37508
37537
|
server.registerTool("gog_drive_revisions_list", {
|
|
37509
37538
|
description: "List a file's revision history \u2014 paged revision metadata plus provider export links for each revision.",
|
|
37510
|
-
annotations: { readOnlyHint: true },
|
|
37539
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37511
37540
|
inputSchema: external_exports.object({
|
|
37512
37541
|
fileId: external_exports.string().describe("File ID"),
|
|
37513
37542
|
...paginationParams,
|
|
@@ -37520,7 +37549,7 @@ function registerExtraDriveTools(server) {
|
|
|
37520
37549
|
});
|
|
37521
37550
|
server.registerTool("gog_drive_revisions_get", {
|
|
37522
37551
|
description: "Get one revision of a Drive file by revision ID (metadata + export links). Find revision IDs with gog_drive_revisions_list.",
|
|
37523
|
-
annotations: { readOnlyHint: true },
|
|
37552
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37524
37553
|
inputSchema: external_exports.object({
|
|
37525
37554
|
fileId: external_exports.string().describe("File ID"),
|
|
37526
37555
|
revisionId: external_exports.string().describe("Revision ID"),
|
|
@@ -37531,7 +37560,7 @@ function registerExtraDriveTools(server) {
|
|
|
37531
37560
|
});
|
|
37532
37561
|
server.registerTool("gog_drive_shortcut_create", {
|
|
37533
37562
|
description: "Create a Drive shortcut to a file or folder inside a destination folder. Shortcuts are classified distinctly in listing/tree output and are never followed by tree scans.",
|
|
37534
|
-
annotations: { destructiveHint: false },
|
|
37563
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
37535
37564
|
inputSchema: external_exports.object({
|
|
37536
37565
|
targetId: external_exports.string().describe("File or folder ID the shortcut points to"),
|
|
37537
37566
|
parent: external_exports.string().describe("Destination folder ID for the shortcut"),
|
|
@@ -37545,7 +37574,7 @@ function registerExtraDriveTools(server) {
|
|
|
37545
37574
|
});
|
|
37546
37575
|
server.registerTool("gog_drive_labels_list", {
|
|
37547
37576
|
description: "List Drive label schemas available to the account.",
|
|
37548
|
-
annotations: { readOnlyHint: true },
|
|
37577
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37549
37578
|
inputSchema: external_exports.object({
|
|
37550
37579
|
language: external_exports.string().optional().describe("BCP-47 language code"),
|
|
37551
37580
|
view: external_exports.enum(["LABEL_VIEW_BASIC", "LABEL_VIEW_FULL"]).optional().describe("Label view (default: LABEL_VIEW_BASIC)"),
|
|
@@ -37571,7 +37600,7 @@ function registerExtraDriveTools(server) {
|
|
|
37571
37600
|
});
|
|
37572
37601
|
server.registerTool("gog_drive_labels_get", {
|
|
37573
37602
|
description: 'Get a single Drive label schema by name (e.g. "labels/abc123").',
|
|
37574
|
-
annotations: { readOnlyHint: true },
|
|
37603
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37575
37604
|
inputSchema: external_exports.object({
|
|
37576
37605
|
name: external_exports.string().describe("Label schema name (e.g. labels/abc123)"),
|
|
37577
37606
|
language: external_exports.string().optional().describe("BCP-47 language code"),
|
|
@@ -37588,7 +37617,7 @@ function registerExtraDriveTools(server) {
|
|
|
37588
37617
|
});
|
|
37589
37618
|
server.registerTool("gog_drive_labels_file_list", {
|
|
37590
37619
|
description: "List labels applied to a Drive file.",
|
|
37591
|
-
annotations: { readOnlyHint: true },
|
|
37620
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37592
37621
|
inputSchema: external_exports.object({
|
|
37593
37622
|
fileId: external_exports.string().describe("File ID"),
|
|
37594
37623
|
max: external_exports.number().optional().describe("Max results (default: 100)"),
|
|
@@ -37605,7 +37634,7 @@ function registerExtraDriveTools(server) {
|
|
|
37605
37634
|
});
|
|
37606
37635
|
server.registerTool("gog_drive_labels_file_apply", {
|
|
37607
37636
|
description: 'Apply or update a label on a Drive file, optionally setting field values. Each field flag takes "fieldId=value" entries (repeatable). selection/integer/date/user values may be comma-separated within one entry for multi-valued fields.',
|
|
37608
|
-
annotations: { destructiveHint: false },
|
|
37637
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
37609
37638
|
inputSchema: external_exports.object({
|
|
37610
37639
|
fileId: external_exports.string().describe("File ID"),
|
|
37611
37640
|
labelId: external_exports.string().describe("Label ID to apply"),
|
|
@@ -37631,7 +37660,7 @@ function registerExtraDriveTools(server) {
|
|
|
37631
37660
|
});
|
|
37632
37661
|
server.registerTool("gog_drive_labels_file_remove", {
|
|
37633
37662
|
description: "Remove a label from a Drive file.",
|
|
37634
|
-
annotations: { destructiveHint: true },
|
|
37663
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
37635
37664
|
inputSchema: external_exports.object({
|
|
37636
37665
|
fileId: external_exports.string().describe("File ID"),
|
|
37637
37666
|
labelId: external_exports.string().describe("Label ID to remove"),
|
|
@@ -37642,7 +37671,7 @@ function registerExtraDriveTools(server) {
|
|
|
37642
37671
|
});
|
|
37643
37672
|
server.registerTool("gog_drive_activity", {
|
|
37644
37673
|
description: "Query the Drive Activity API for audit events (edits, creates, deletes, moves, shares, etc.) scoped to a file or folder and/or time range.",
|
|
37645
|
-
annotations: { readOnlyHint: true },
|
|
37674
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37646
37675
|
inputSchema: external_exports.object({
|
|
37647
37676
|
file: external_exports.string().optional().describe("Drive file ID to query"),
|
|
37648
37677
|
folder: external_exports.string().optional().describe("Drive folder ID; includes descendants"),
|
|
@@ -37674,7 +37703,7 @@ function registerExtraDriveTools(server) {
|
|
|
37674
37703
|
});
|
|
37675
37704
|
server.registerTool("gog_drive_audit_sharing", {
|
|
37676
37705
|
description: "Audit Drive sharing without mutation \u2014 find public (anyone-with-link) or external permissions across a folder tree or a single file.",
|
|
37677
|
-
annotations: { readOnlyHint: true },
|
|
37706
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37678
37707
|
inputSchema: external_exports.object({
|
|
37679
37708
|
file: external_exports.string().optional().describe("Audit one file ID instead of a folder tree"),
|
|
37680
37709
|
parent: external_exports.string().optional().describe("Folder ID to scan (default: root)"),
|
|
@@ -37700,7 +37729,7 @@ function registerExtraDriveTools(server) {
|
|
|
37700
37729
|
});
|
|
37701
37730
|
server.registerTool("gog_drive_audit_user", {
|
|
37702
37731
|
description: "Audit Drive sharing without mutation \u2014 find permissions granted to a specific user across a folder tree or a single file.",
|
|
37703
|
-
annotations: { readOnlyHint: true },
|
|
37732
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
37704
37733
|
inputSchema: external_exports.object({
|
|
37705
37734
|
user: external_exports.string().describe("User email to audit permissions for"),
|
|
37706
37735
|
file: external_exports.string().optional().describe("Audit one file ID instead of a folder tree"),
|
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.7.
|
|
6
|
+
"version": "4.7.1",
|
|
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",
|
|
@@ -39,7 +39,10 @@
|
|
|
39
39
|
"env": {
|
|
40
40
|
"GOG_ACCOUNT": "${user_config.gog_account}",
|
|
41
41
|
"GOG_PATH": "${user_config.gog_path}",
|
|
42
|
-
"GOG_FILE_ROOTS": "${user_config.gog_file_roots}"
|
|
42
|
+
"GOG_FILE_ROOTS": "${user_config.gog_file_roots}",
|
|
43
|
+
"DISPLAY_TZ": "${user_config.display_tz}",
|
|
44
|
+
"GOG_READONLY": "${user_config.gog_readonly}",
|
|
45
|
+
"GOG_TIMEZONE": "${user_config.gog_timezone}"
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
},
|
|
@@ -61,6 +64,24 @@
|
|
|
61
64
|
"title": "Allowed file directories",
|
|
62
65
|
"description": "Directories tools may read files from (attachments, uploads) or write files to (exports, downloads), separated by ':' (';' on Windows). Optional — defaults to ~/gogcli-mcp-files",
|
|
63
66
|
"required": false
|
|
67
|
+
},
|
|
68
|
+
"display_tz": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"title": "Display time zone",
|
|
71
|
+
"description": "zone for *Display fields and for interpreting naive gog values; defaults to America/New_York",
|
|
72
|
+
"required": false
|
|
73
|
+
},
|
|
74
|
+
"gog_readonly": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"title": "Read-only mode",
|
|
77
|
+
"description": "block all mutating gog API requests (injects gog's --readonly); set to 0/false/no/off (or unset) to allow writes",
|
|
78
|
+
"required": false
|
|
79
|
+
},
|
|
80
|
+
"gog_timezone": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"title": "gog time zone",
|
|
83
|
+
"description": "zone gog formats naive dates in; unset, runner.ts hands gog DISPLAY_TZ so the two cannot diverge",
|
|
84
|
+
"required": false
|
|
64
85
|
}
|
|
65
86
|
},
|
|
66
87
|
"tools": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp-drive",
|
|
3
|
-
"version": "4.7.
|
|
3
|
+
"version": "4.7.1",
|
|
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": "^
|
|
27
|
+
"@chrischall/mcp-utils": "^3.0.0",
|
|
28
28
|
"@modelcontextprotocol/server": "^2.3.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.7.
|
|
10
|
+
"version": "4.7.1",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "gogcli-mcp-drive",
|
|
15
|
-
"version": "4.7.
|
|
15
|
+
"version": "4.7.1",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
|
@@ -28,6 +28,24 @@
|
|
|
28
28
|
"description": "Override path to the gogcli binary (auto-discovered otherwise)",
|
|
29
29
|
"isRequired": false,
|
|
30
30
|
"format": "string"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "DISPLAY_TZ",
|
|
34
|
+
"description": "zone for *Display fields and for interpreting naive gog values; defaults to America/New_York",
|
|
35
|
+
"isRequired": false,
|
|
36
|
+
"format": "string"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "GOG_READONLY",
|
|
40
|
+
"description": "block all mutating gog API requests (injects gog's --readonly); set to 0/false/no/off (or unset) to allow writes",
|
|
41
|
+
"isRequired": false,
|
|
42
|
+
"format": "string"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "GOG_TIMEZONE",
|
|
46
|
+
"description": "zone gog formats naive dates in; unset, runner.ts hands gog DISPLAY_TZ so the two cannot diverge",
|
|
47
|
+
"isRequired": false,
|
|
48
|
+
"format": "string"
|
|
31
49
|
}
|
|
32
50
|
]
|
|
33
51
|
}
|
package/src/tools/drive-extra.ts
CHANGED
|
@@ -26,7 +26,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
26
26
|
server.registerTool('gog_drive_download', {
|
|
27
27
|
description: 'Download a Drive file to the local filesystem. For Google Docs formats, specify an export format (pdf, csv, xlsx, pptx, txt, png, docx, md).',
|
|
28
28
|
// Writes a file on the gog host and can overwrite one: not read-only (audit SEC-4).
|
|
29
|
-
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
29
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
30
30
|
inputSchema: z.object({
|
|
31
31
|
fileId: z.string().describe('File ID to download'),
|
|
32
32
|
out: z.string().optional().describe('Output file path (default: gogcli config dir)'),
|
|
@@ -55,7 +55,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
55
55
|
'No read tool surfaces the version number: fetch it immediately before uploading with ' +
|
|
56
56
|
'gog_drive_run { subcommand: "raw", args: ["<fileId>", "--fields=version"] } — it comes back as a JSON string, so pass it on as a number. ' +
|
|
57
57
|
'Conditional replacement refuses Google Workspace files (Docs/Sheets/Slides), which have no replaceable binary content.',
|
|
58
|
-
annotations: { destructiveHint: false },
|
|
58
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
59
59
|
inputSchema: z.object({
|
|
60
60
|
localPath: z.string().optional().describe('Path to the file to upload, resolved ON THE GOG SERVER\'s filesystem — NOT this client\'s. Only usable when gog runs on the same machine you do (local stdio); on a hosted deployment (e.g. mcp-host) this path does not exist and the call fails with "no such file or directory" — use content there. Exactly one of localPath / content is required. Must be inside the server\'s GOG_FILE_ROOTS directories (default ~/gogcli-mcp-files).'),
|
|
61
61
|
content: z.string().optional().describe('The file\'s bytes, base64-encoded (standard alphabet, with padding) — upload a file you hold without it existing anywhere on the gog server. This is the only route that works when the caller and gog share no filesystem. Requires name (there is no path to take a filename from). Max 8 MiB; for anything larger use localPath from a local deployment. Exactly one of localPath / content is required — supplying both is an error, not a precedence rule.'),
|
|
@@ -117,7 +117,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
117
117
|
|
|
118
118
|
server.registerTool('gog_drive_sync_push', {
|
|
119
119
|
description: 'Recursively push a local directory\'s contents into an existing Drive folder — uploads new files, updates changed ones (matched by name + MD5), and creates missing subfolders. One-way and additive: it never deletes anything on Drive. Use dryRun to preview the planned actions first.',
|
|
120
|
-
annotations: { destructiveHint: true },
|
|
120
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
121
121
|
inputSchema: z.object({
|
|
122
122
|
localPath: z.string().describe('Local directory to push (its contents are mirrored into the parent folder). Must be inside the server\'s GOG_FILE_ROOTS directories (default ~/gogcli-mcp-files).'),
|
|
123
123
|
parent: z.string().describe('Existing destination Drive folder ID'),
|
|
@@ -135,7 +135,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
135
135
|
|
|
136
136
|
server.registerTool('gog_drive_copy', {
|
|
137
137
|
description: 'Copy a Drive file to a new file with the given name.',
|
|
138
|
-
annotations: { destructiveHint: false },
|
|
138
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
139
139
|
inputSchema: z.object({
|
|
140
140
|
fileId: z.string().describe('File ID to copy'),
|
|
141
141
|
name: z.string().describe('Name for the new copy'),
|
|
@@ -150,7 +150,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
150
150
|
|
|
151
151
|
server.registerTool('gog_drive_url', {
|
|
152
152
|
description: 'Print shareable web URLs for one or more Drive files.',
|
|
153
|
-
annotations: { readOnlyHint: true },
|
|
153
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
154
154
|
inputSchema: z.object({
|
|
155
155
|
fileIds: z.array(z.string()).min(1).describe('One or more file IDs to get URLs for'),
|
|
156
156
|
account: accountParam,
|
|
@@ -161,7 +161,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
161
161
|
|
|
162
162
|
server.registerTool('gog_drive_permissions', {
|
|
163
163
|
description: 'List permissions on a Drive file.',
|
|
164
|
-
annotations: { readOnlyHint: true },
|
|
164
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
165
165
|
inputSchema: z.object({
|
|
166
166
|
fileId: z.string().describe('File ID'),
|
|
167
167
|
max: z.number().optional().describe('Max results (default: 100)'),
|
|
@@ -179,7 +179,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
179
179
|
|
|
180
180
|
server.registerTool('gog_drive_unshare', {
|
|
181
181
|
description: 'Remove a permission from a Drive file. Get the permissionId from gog_drive_permissions.',
|
|
182
|
-
annotations: { destructiveHint: true },
|
|
182
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
183
183
|
inputSchema: z.object({
|
|
184
184
|
fileId: z.string().describe('File ID'),
|
|
185
185
|
permissionId: z.string().describe('Permission ID to remove'),
|
|
@@ -191,7 +191,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
191
191
|
|
|
192
192
|
server.registerTool('gog_drive_drives_list', {
|
|
193
193
|
description: 'List shared drives (Team Drives) accessible to the account.',
|
|
194
|
-
annotations: { readOnlyHint: true },
|
|
194
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
195
195
|
inputSchema: z.object({
|
|
196
196
|
max: z.number().optional().describe('Max results (default: 100, max allowed: 100)'),
|
|
197
197
|
pageToken: pageTokenParam,
|
|
@@ -212,7 +212,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
212
212
|
|
|
213
213
|
server.registerTool('gog_drive_comments_list', {
|
|
214
214
|
description: 'List comments on a Drive file.',
|
|
215
|
-
annotations: { readOnlyHint: true },
|
|
215
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
216
216
|
inputSchema: z.object({
|
|
217
217
|
fileId: z.string().describe('File ID'),
|
|
218
218
|
since: z.string().optional().describe('Only return comments modified at or after this RFC3339 timestamp (e.g. 2026-06-01T00:00:00Z)'),
|
|
@@ -230,7 +230,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
230
230
|
|
|
231
231
|
server.registerTool('gog_drive_comments_get', {
|
|
232
232
|
description: 'Get a single comment on a Drive file by ID.',
|
|
233
|
-
annotations: { readOnlyHint: true },
|
|
233
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
234
234
|
inputSchema: z.object({
|
|
235
235
|
fileId: z.string().describe('File ID'),
|
|
236
236
|
commentId: z.string().describe('Comment ID'),
|
|
@@ -244,7 +244,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
244
244
|
description: 'Add a new comment to a Drive file. Drive notifies the file\'s owner and everyone the text +mentions, so '
|
|
245
245
|
+ 'this reads the file and asks the MCP host to show the user a confirmation prompt with the file, the text and who '
|
|
246
246
|
+ 'is mentioned first; nothing is posted unless they accept.' + CONFIRM_FALLBACK_DESCRIPTION,
|
|
247
|
-
annotations: { destructiveHint: false },
|
|
247
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
248
248
|
inputSchema: z.object({
|
|
249
249
|
fileId: z.string().describe('File ID'),
|
|
250
250
|
content: z.string().describe('Comment text'),
|
|
@@ -264,6 +264,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
264
264
|
details: { file, textPreview: bodyPreview(content), mentions },
|
|
265
265
|
unsupportedNote: 'Ask the user to comment from Google Drive.',
|
|
266
266
|
fallback: {
|
|
267
|
+
args: { fileId, content, account },
|
|
267
268
|
tool: 'gog_drive_comments_add',
|
|
268
269
|
account,
|
|
269
270
|
confirmToken,
|
|
@@ -279,7 +280,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
279
280
|
|
|
280
281
|
server.registerTool('gog_drive_comments_update', {
|
|
281
282
|
description: 'Update the text of an existing comment.',
|
|
282
|
-
annotations: { destructiveHint: false },
|
|
283
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
283
284
|
inputSchema: z.object({
|
|
284
285
|
fileId: z.string().describe('File ID'),
|
|
285
286
|
commentId: z.string().describe('Comment ID to update'),
|
|
@@ -292,7 +293,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
292
293
|
|
|
293
294
|
server.registerTool('gog_drive_comments_delete', {
|
|
294
295
|
description: 'Delete a comment from a Drive file.',
|
|
295
|
-
annotations: { destructiveHint: true },
|
|
296
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
296
297
|
inputSchema: z.object({
|
|
297
298
|
fileId: z.string().describe('File ID'),
|
|
298
299
|
commentId: z.string().describe('Comment ID to delete'),
|
|
@@ -307,7 +308,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
307
308
|
+ 'Everyone on the thread and anyone the reply +mentions is notified, so this reads the file and the comment and '
|
|
308
309
|
+ 'asks the MCP host to show the user a confirmation prompt with both and the reply first; nothing is posted unless '
|
|
309
310
|
+ 'they accept.' + CONFIRM_FALLBACK_DESCRIPTION,
|
|
310
|
-
annotations: { destructiveHint: false },
|
|
311
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
311
312
|
inputSchema: z.object({
|
|
312
313
|
fileId: z.string().describe('File ID'),
|
|
313
314
|
commentId: z.string().describe('Comment ID to reply to'),
|
|
@@ -334,6 +335,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
334
335
|
details: { file, replyingTo, textPreview: bodyPreview(content), mentions, ...(action ? { action } : {}) },
|
|
335
336
|
unsupportedNote: 'Ask the user to reply from Google Drive.',
|
|
336
337
|
fallback: {
|
|
338
|
+
args: { fileId, commentId, content, action, account },
|
|
337
339
|
tool: 'gog_drive_comments_reply',
|
|
338
340
|
account,
|
|
339
341
|
confirmToken,
|
|
@@ -349,7 +351,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
349
351
|
|
|
350
352
|
server.registerTool('gog_drive_comments_resolve', {
|
|
351
353
|
description: 'Resolve a comment on a Drive file (mark as done) without posting a reply. To resolve while replying, use gog_drive_comments_reply with action: "resolve".',
|
|
352
|
-
annotations: { destructiveHint: true },
|
|
354
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
353
355
|
inputSchema: z.object({
|
|
354
356
|
fileId: z.string().describe('File ID'),
|
|
355
357
|
commentId: z.string().describe('Comment ID to resolve'),
|
|
@@ -361,7 +363,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
361
363
|
|
|
362
364
|
server.registerTool('gog_drive_comments_reopen', {
|
|
363
365
|
description: 'Reopen a previously resolved comment on a Drive file. To reopen while replying, use gog_drive_comments_reply with action: "reopen".',
|
|
364
|
-
annotations: { destructiveHint: true },
|
|
366
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
365
367
|
inputSchema: z.object({
|
|
366
368
|
fileId: z.string().describe('File ID'),
|
|
367
369
|
commentId: z.string().describe('Comment ID to reopen'),
|
|
@@ -375,7 +377,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
375
377
|
|
|
376
378
|
server.registerTool('gog_drive_du', {
|
|
377
379
|
description: 'Summarize Drive folder sizes (disk-usage style) starting from a folder.',
|
|
378
|
-
annotations: { readOnlyHint: true },
|
|
380
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
379
381
|
inputSchema: z.object({
|
|
380
382
|
parent: z.string().optional().describe('Folder ID to start from (default: root)'),
|
|
381
383
|
depth: z.number().optional().describe('Depth for folder totals (default: 1)'),
|
|
@@ -398,7 +400,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
398
400
|
|
|
399
401
|
server.registerTool('gog_drive_tree', {
|
|
400
402
|
description: 'Print a read-only folder tree starting from a folder.',
|
|
401
|
-
annotations: { readOnlyHint: true },
|
|
403
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
402
404
|
inputSchema: z.object({
|
|
403
405
|
parent: z.string().optional().describe('Folder ID to start from (default: root)'),
|
|
404
406
|
depth: z.number().optional().describe('Max depth (0 = unlimited; default: 2)'),
|
|
@@ -417,7 +419,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
417
419
|
|
|
418
420
|
server.registerTool('gog_drive_changes_start_token', {
|
|
419
421
|
description: 'Get a Drive changes start page token — the cursor you pass to gog_drive_changes_list to enumerate changes since this point.',
|
|
420
|
-
annotations: { readOnlyHint: true },
|
|
422
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
421
423
|
inputSchema: z.object({
|
|
422
424
|
drive: z.string().optional().describe('Shared drive ID for a shared-drive change log'),
|
|
423
425
|
account: accountParam,
|
|
@@ -430,7 +432,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
430
432
|
|
|
431
433
|
server.registerTool('gog_drive_changes_list', {
|
|
432
434
|
description: 'List Drive changes since a page token (for sync/automation). Get the initial token from gog_drive_changes_start_token; the response includes a newStartPageToken to persist for the next poll.',
|
|
433
|
-
annotations: { readOnlyHint: true },
|
|
435
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
434
436
|
inputSchema: z.object({
|
|
435
437
|
token: z.string().describe('Start page token or next page token'),
|
|
436
438
|
max: z.number().optional().describe('Max results (default: 100)'),
|
|
@@ -456,7 +458,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
456
458
|
|
|
457
459
|
server.registerTool('gog_drive_revisions_list', {
|
|
458
460
|
description: 'List a file\'s revision history — paged revision metadata plus provider export links for each revision.',
|
|
459
|
-
annotations: { readOnlyHint: true },
|
|
461
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
460
462
|
inputSchema: z.object({
|
|
461
463
|
fileId: z.string().describe('File ID'),
|
|
462
464
|
...paginationParams,
|
|
@@ -470,7 +472,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
470
472
|
|
|
471
473
|
server.registerTool('gog_drive_revisions_get', {
|
|
472
474
|
description: 'Get one revision of a Drive file by revision ID (metadata + export links). Find revision IDs with gog_drive_revisions_list.',
|
|
473
|
-
annotations: { readOnlyHint: true },
|
|
475
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
474
476
|
inputSchema: z.object({
|
|
475
477
|
fileId: z.string().describe('File ID'),
|
|
476
478
|
revisionId: z.string().describe('Revision ID'),
|
|
@@ -482,7 +484,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
482
484
|
|
|
483
485
|
server.registerTool('gog_drive_shortcut_create', {
|
|
484
486
|
description: 'Create a Drive shortcut to a file or folder inside a destination folder. Shortcuts are classified distinctly in listing/tree output and are never followed by tree scans.',
|
|
485
|
-
annotations: { destructiveHint: false },
|
|
487
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
486
488
|
inputSchema: z.object({
|
|
487
489
|
targetId: z.string().describe('File or folder ID the shortcut points to'),
|
|
488
490
|
parent: z.string().describe('Destination folder ID for the shortcut'),
|
|
@@ -497,7 +499,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
497
499
|
|
|
498
500
|
server.registerTool('gog_drive_labels_list', {
|
|
499
501
|
description: 'List Drive label schemas available to the account.',
|
|
500
|
-
annotations: { readOnlyHint: true },
|
|
502
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
501
503
|
inputSchema: z.object({
|
|
502
504
|
language: z.string().optional().describe('BCP-47 language code'),
|
|
503
505
|
view: z.enum(['LABEL_VIEW_BASIC', 'LABEL_VIEW_FULL']).optional().describe('Label view (default: LABEL_VIEW_BASIC)'),
|
|
@@ -524,7 +526,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
524
526
|
|
|
525
527
|
server.registerTool('gog_drive_labels_get', {
|
|
526
528
|
description: 'Get a single Drive label schema by name (e.g. "labels/abc123").',
|
|
527
|
-
annotations: { readOnlyHint: true },
|
|
529
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
528
530
|
inputSchema: z.object({
|
|
529
531
|
name: z.string().describe('Label schema name (e.g. labels/abc123)'),
|
|
530
532
|
language: z.string().optional().describe('BCP-47 language code'),
|
|
@@ -542,7 +544,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
542
544
|
|
|
543
545
|
server.registerTool('gog_drive_labels_file_list', {
|
|
544
546
|
description: 'List labels applied to a Drive file.',
|
|
545
|
-
annotations: { readOnlyHint: true },
|
|
547
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
546
548
|
inputSchema: z.object({
|
|
547
549
|
fileId: z.string().describe('File ID'),
|
|
548
550
|
max: z.number().optional().describe('Max results (default: 100)'),
|
|
@@ -560,7 +562,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
560
562
|
|
|
561
563
|
server.registerTool('gog_drive_labels_file_apply', {
|
|
562
564
|
description: 'Apply or update a label on a Drive file, optionally setting field values. Each field flag takes "fieldId=value" entries (repeatable). selection/integer/date/user values may be comma-separated within one entry for multi-valued fields.',
|
|
563
|
-
annotations: { destructiveHint: false },
|
|
565
|
+
annotations: { destructiveHint: false, openWorldHint: true },
|
|
564
566
|
inputSchema: z.object({
|
|
565
567
|
fileId: z.string().describe('File ID'),
|
|
566
568
|
labelId: z.string().describe('Label ID to apply'),
|
|
@@ -587,7 +589,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
587
589
|
|
|
588
590
|
server.registerTool('gog_drive_labels_file_remove', {
|
|
589
591
|
description: 'Remove a label from a Drive file.',
|
|
590
|
-
annotations: { destructiveHint: true },
|
|
592
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
591
593
|
inputSchema: z.object({
|
|
592
594
|
fileId: z.string().describe('File ID'),
|
|
593
595
|
labelId: z.string().describe('Label ID to remove'),
|
|
@@ -599,7 +601,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
599
601
|
|
|
600
602
|
server.registerTool('gog_drive_activity', {
|
|
601
603
|
description: 'Query the Drive Activity API for audit events (edits, creates, deletes, moves, shares, etc.) scoped to a file or folder and/or time range.',
|
|
602
|
-
annotations: { readOnlyHint: true },
|
|
604
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
603
605
|
inputSchema: z.object({
|
|
604
606
|
file: z.string().optional().describe('Drive file ID to query'),
|
|
605
607
|
folder: z.string().optional().describe('Drive folder ID; includes descendants'),
|
|
@@ -632,7 +634,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
632
634
|
|
|
633
635
|
server.registerTool('gog_drive_audit_sharing', {
|
|
634
636
|
description: 'Audit Drive sharing without mutation — find public (anyone-with-link) or external permissions across a folder tree or a single file.',
|
|
635
|
-
annotations: { readOnlyHint: true },
|
|
637
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
636
638
|
inputSchema: z.object({
|
|
637
639
|
file: z.string().optional().describe('Audit one file ID instead of a folder tree'),
|
|
638
640
|
parent: z.string().optional().describe('Folder ID to scan (default: root)'),
|
|
@@ -659,7 +661,7 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
659
661
|
|
|
660
662
|
server.registerTool('gog_drive_audit_user', {
|
|
661
663
|
description: 'Audit Drive sharing without mutation — find permissions granted to a specific user across a folder tree or a single file.',
|
|
662
|
-
annotations: { readOnlyHint: true },
|
|
664
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
663
665
|
inputSchema: z.object({
|
|
664
666
|
user: z.string().describe('User email to audit permissions for'),
|
|
665
667
|
file: z.string().optional().describe('Audit one file ID instead of a folder tree'),
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { authToolsFor, registerDriveTools } from '../../gogcli-mcp/src/lib.js';
|
|
2
|
+
import { registerExtraDriveTools } from '../src/tools/drive-extra.js';
|
|
3
|
+
import { describeToolAnnotations, LOCAL_AUTH_TOOLS } from '../../gogcli-mcp/tests/support/tool-annotations.js';
|
|
4
|
+
|
|
5
|
+
// The same registrar list src/index.ts hands to runMcp.
|
|
6
|
+
describeToolAnnotations([authToolsFor('drive,driveactivity,drivelabels'), registerDriveTools, registerExtraDriveTools] as never, {
|
|
7
|
+
tools: 50,
|
|
8
|
+
destructive: 13,
|
|
9
|
+
local: [...LOCAL_AUTH_TOOLS],
|
|
10
|
+
});
|