gogcli-mcp-drive 2.30.0 → 4.0.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.
@@ -1,4 +1,4 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import { McpServer } from '@modelcontextprotocol/server';
2
2
  import { z } from 'zod';
3
3
  import { accountParam, runOrDiagnose, paginationParams, pushPaginationFlags, pageTokenParam, pageAliasParam, resolvePageToken, inlineFileArg} from '../../../gogcli-mcp/src/lib.js';
4
4
  import type { GogArg } from '../../../gogcli-mcp/src/lib.js';
@@ -7,13 +7,13 @@ export function registerExtraDriveTools(server: McpServer): void {
7
7
  server.registerTool('gog_drive_download', {
8
8
  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).',
9
9
  annotations: { readOnlyHint: true },
10
- inputSchema: {
10
+ inputSchema: z.object({
11
11
  fileId: z.string().describe('File ID to download'),
12
12
  out: z.string().optional().describe('Output file path (default: gogcli config dir)'),
13
13
  format: z.string().optional().describe('Export format for Google Docs: pdf, csv, xlsx, pptx, txt, png, docx, md (default: inferred)'),
14
14
  overwrite: z.boolean().optional().describe('Overwrite the output file if it already exists (gog refuses otherwise)'),
15
15
  account: accountParam,
16
- },
16
+ }),
17
17
  }, async ({ fileId, out, format, overwrite, account }) => {
18
18
  const args = ['drive', 'download', fileId];
19
19
  if (out) args.push(`--out=${out}`);
@@ -35,8 +35,8 @@ export function registerExtraDriveTools(server: McpServer): void {
35
35
  'gog_drive_run { subcommand: "raw", args: ["<fileId>", "--fields=version"] } — it comes back as a JSON string, so pass it on as a number. ' +
36
36
  'Conditional replacement refuses Google Workspace files (Docs/Sheets/Slides), which have no replaceable binary content.',
37
37
  annotations: { destructiveHint: true },
38
- inputSchema: {
39
- 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 the hosted connector or any GOG_RUNNER_URL backend 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.'),
38
+ inputSchema: z.object({
39
+ 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.'),
40
40
  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.'),
41
41
  name: z.string().optional().describe('Override filename (create) or rename target (replace)'),
42
42
  parent: z.string().optional().describe('Destination folder ID (create only)'),
@@ -51,7 +51,7 @@ export function registerExtraDriveTools(server: McpServer): void {
51
51
  convert: z.boolean().optional().describe('Auto-convert to native Google format based on file extension (create only)'),
52
52
  convertTo: z.string().optional().describe('Convert to a specific Google format: doc | sheet | slides (create only)'),
53
53
  account: accountParam,
54
- },
54
+ }),
55
55
  }, async ({ localPath, content, name, parent, replace, ifVersion, mimeType, keepRevisionForever, convert, convertTo, account }) => {
56
56
  if (ifVersion !== undefined && !replace) {
57
57
  throw new Error('ifVersion requires replace: a version precondition only applies when replacing an existing Drive file.');
@@ -97,13 +97,13 @@ export function registerExtraDriveTools(server: McpServer): void {
97
97
  server.registerTool('gog_drive_sync_push', {
98
98
  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.',
99
99
  annotations: { destructiveHint: true },
100
- inputSchema: {
100
+ inputSchema: z.object({
101
101
  localPath: z.string().describe('Local directory to push (its contents are mirrored into the parent folder)'),
102
102
  parent: z.string().describe('Existing destination Drive folder ID'),
103
103
  dryRun: z.boolean().optional().describe('Preview the planned actions without making any changes'),
104
104
  allDrives: z.boolean().optional().describe('Include shared drives (default: true; set false for My Drive only)'),
105
105
  account: accountParam,
106
- },
106
+ }),
107
107
  }, async ({ localPath, parent, dryRun, allDrives, account }) => {
108
108
  const args = ['drive', 'sync', 'push', localPath, `--parent=${parent}`];
109
109
  if (dryRun) args.push('--dry-run');
@@ -113,12 +113,12 @@ export function registerExtraDriveTools(server: McpServer): void {
113
113
 
114
114
  server.registerTool('gog_drive_copy', {
115
115
  description: 'Copy a Drive file to a new file with the given name.',
116
- inputSchema: {
116
+ inputSchema: z.object({
117
117
  fileId: z.string().describe('File ID to copy'),
118
118
  name: z.string().describe('Name for the new copy'),
119
119
  parent: z.string().optional().describe('Destination folder ID'),
120
120
  account: accountParam,
121
- },
121
+ }),
122
122
  }, async ({ fileId, name, parent, account }) => {
123
123
  const args = ['drive', 'copy', fileId, name];
124
124
  if (parent) args.push(`--parent=${parent}`);
@@ -128,10 +128,10 @@ export function registerExtraDriveTools(server: McpServer): void {
128
128
  server.registerTool('gog_drive_url', {
129
129
  description: 'Print shareable web URLs for one or more Drive files.',
130
130
  annotations: { readOnlyHint: true },
131
- inputSchema: {
131
+ inputSchema: z.object({
132
132
  fileIds: z.array(z.string()).min(1).describe('One or more file IDs to get URLs for'),
133
133
  account: accountParam,
134
- },
134
+ }),
135
135
  }, async ({ fileIds, account }) => {
136
136
  return runOrDiagnose(['drive', 'url', ...fileIds], { account });
137
137
  });
@@ -139,13 +139,13 @@ export function registerExtraDriveTools(server: McpServer): void {
139
139
  server.registerTool('gog_drive_permissions', {
140
140
  description: 'List permissions on a Drive file.',
141
141
  annotations: { readOnlyHint: true },
142
- inputSchema: {
142
+ inputSchema: z.object({
143
143
  fileId: z.string().describe('File ID'),
144
144
  max: z.number().optional().describe('Max results (default: 100)'),
145
145
  pageToken: pageTokenParam,
146
146
  page: pageAliasParam,
147
147
  account: accountParam,
148
- },
148
+ }),
149
149
  }, async ({ fileId, max, pageToken, page, account }) => {
150
150
  const args = ['drive', 'permissions', fileId];
151
151
  if (max !== undefined) args.push(`--max=${max}`);
@@ -157,11 +157,11 @@ export function registerExtraDriveTools(server: McpServer): void {
157
157
  server.registerTool('gog_drive_unshare', {
158
158
  description: 'Remove a permission from a Drive file. Get the permissionId from gog_drive_permissions.',
159
159
  annotations: { destructiveHint: true },
160
- inputSchema: {
160
+ inputSchema: z.object({
161
161
  fileId: z.string().describe('File ID'),
162
162
  permissionId: z.string().describe('Permission ID to remove'),
163
163
  account: accountParam,
164
- },
164
+ }),
165
165
  }, async ({ fileId, permissionId, account }) => {
166
166
  return runOrDiagnose(['drive', 'unshare', fileId, permissionId, '--force'], { account }); // gog gates this op; without --force the runner's --no-input makes it refuse
167
167
  });
@@ -169,14 +169,14 @@ export function registerExtraDriveTools(server: McpServer): void {
169
169
  server.registerTool('gog_drive_drives_list', {
170
170
  description: 'List shared drives (Team Drives) accessible to the account.',
171
171
  annotations: { readOnlyHint: true },
172
- inputSchema: {
172
+ inputSchema: z.object({
173
173
  max: z.number().optional().describe('Max results (default: 100, max allowed: 100)'),
174
174
  pageToken: pageTokenParam,
175
175
  page: pageAliasParam,
176
176
  all: z.boolean().optional().describe('Fetch all pages'),
177
177
  query: z.string().optional().describe('Search query for filtering shared drives'),
178
178
  account: accountParam,
179
- },
179
+ }),
180
180
  }, async ({ max, pageToken, page, all, query, account }) => {
181
181
  const args = ['drive', 'drives'];
182
182
  if (max !== undefined) args.push(`--max=${max}`);
@@ -190,13 +190,13 @@ export function registerExtraDriveTools(server: McpServer): void {
190
190
  server.registerTool('gog_drive_comments_list', {
191
191
  description: 'List comments on a Drive file.',
192
192
  annotations: { readOnlyHint: true },
193
- inputSchema: {
193
+ inputSchema: z.object({
194
194
  fileId: z.string().describe('File ID'),
195
195
  since: z.string().optional().describe('Only return comments modified at or after this RFC3339 timestamp (e.g. 2026-06-01T00:00:00Z)'),
196
196
  includeQuoted: z.boolean().optional().describe('Include the quoted content each comment is anchored to'),
197
197
  ...paginationParams,
198
198
  account: accountParam,
199
- },
199
+ }),
200
200
  }, async ({ fileId, since, includeQuoted, max, page, all, account }) => {
201
201
  const args = ['drive', 'comments', 'list', fileId];
202
202
  if (since) args.push(`--since=${since}`);
@@ -208,22 +208,22 @@ export function registerExtraDriveTools(server: McpServer): void {
208
208
  server.registerTool('gog_drive_comments_get', {
209
209
  description: 'Get a single comment on a Drive file by ID.',
210
210
  annotations: { readOnlyHint: true },
211
- inputSchema: {
211
+ inputSchema: z.object({
212
212
  fileId: z.string().describe('File ID'),
213
213
  commentId: z.string().describe('Comment ID'),
214
214
  account: accountParam,
215
- },
215
+ }),
216
216
  }, async ({ fileId, commentId, account }) => {
217
217
  return runOrDiagnose(['drive', 'comments', 'get', fileId, commentId], { account });
218
218
  });
219
219
 
220
220
  server.registerTool('gog_drive_comments_add', {
221
221
  description: 'Add a new comment to a Drive file.',
222
- inputSchema: {
222
+ inputSchema: z.object({
223
223
  fileId: z.string().describe('File ID'),
224
224
  content: z.string().describe('Comment text'),
225
225
  account: accountParam,
226
- },
226
+ }),
227
227
  }, async ({ fileId, content, account }) => {
228
228
  return runOrDiagnose(['drive', 'comments', 'create', fileId, content], { account });
229
229
  });
@@ -231,12 +231,12 @@ export function registerExtraDriveTools(server: McpServer): void {
231
231
  server.registerTool('gog_drive_comments_update', {
232
232
  description: 'Update the text of an existing comment.',
233
233
  annotations: { destructiveHint: true },
234
- inputSchema: {
234
+ inputSchema: z.object({
235
235
  fileId: z.string().describe('File ID'),
236
236
  commentId: z.string().describe('Comment ID to update'),
237
237
  content: z.string().describe('New comment text'),
238
238
  account: accountParam,
239
- },
239
+ }),
240
240
  }, async ({ fileId, commentId, content, account }) => {
241
241
  return runOrDiagnose(['drive', 'comments', 'update', fileId, commentId, content], { account });
242
242
  });
@@ -244,24 +244,24 @@ export function registerExtraDriveTools(server: McpServer): void {
244
244
  server.registerTool('gog_drive_comments_delete', {
245
245
  description: 'Delete a comment from a Drive file.',
246
246
  annotations: { destructiveHint: true },
247
- inputSchema: {
247
+ inputSchema: z.object({
248
248
  fileId: z.string().describe('File ID'),
249
249
  commentId: z.string().describe('Comment ID to delete'),
250
250
  account: accountParam,
251
- },
251
+ }),
252
252
  }, async ({ fileId, commentId, account }) => {
253
253
  return runOrDiagnose(['drive', 'comments', 'delete', fileId, commentId, '--force'], { account }); // gog gates this op; without --force the runner's --no-input makes it refuse
254
254
  });
255
255
 
256
256
  server.registerTool('gog_drive_comments_reply', {
257
257
  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 — avoids the older workaround of deleting the comment (which destroys review-thread context).',
258
- inputSchema: {
258
+ inputSchema: z.object({
259
259
  fileId: z.string().describe('File ID'),
260
260
  commentId: z.string().describe('Comment ID to reply to'),
261
261
  content: z.string().describe('Reply text'),
262
262
  action: z.enum(['resolve', 'reopen']).optional().describe('Optional action on the parent comment alongside the reply'),
263
263
  account: accountParam,
264
- },
264
+ }),
265
265
  }, async ({ fileId, commentId, content, action, account }) => {
266
266
  const args = ['drive', 'comments', 'reply', fileId, commentId, content];
267
267
  if (action) args.push(`--action=${action}`);
@@ -271,11 +271,11 @@ export function registerExtraDriveTools(server: McpServer): void {
271
271
  server.registerTool('gog_drive_comments_resolve', {
272
272
  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".',
273
273
  annotations: { destructiveHint: true },
274
- inputSchema: {
274
+ inputSchema: z.object({
275
275
  fileId: z.string().describe('File ID'),
276
276
  commentId: z.string().describe('Comment ID to resolve'),
277
277
  account: accountParam,
278
- },
278
+ }),
279
279
  }, async ({ fileId, commentId, account }) => {
280
280
  return runOrDiagnose(['drive', 'comments', 'resolve', fileId, commentId], { account });
281
281
  });
@@ -283,11 +283,11 @@ export function registerExtraDriveTools(server: McpServer): void {
283
283
  server.registerTool('gog_drive_comments_reopen', {
284
284
  description: 'Reopen a previously resolved comment on a Drive file. To reopen while replying, use gog_drive_comments_reply with action: "reopen".',
285
285
  annotations: { destructiveHint: true },
286
- inputSchema: {
286
+ inputSchema: z.object({
287
287
  fileId: z.string().describe('File ID'),
288
288
  commentId: z.string().describe('Comment ID to reopen'),
289
289
  account: accountParam,
290
- },
290
+ }),
291
291
  }, async ({ fileId, commentId, account }) => {
292
292
  return runOrDiagnose(['drive', 'comments', 'reopen', fileId, commentId], { account });
293
293
  });
@@ -297,7 +297,7 @@ export function registerExtraDriveTools(server: McpServer): void {
297
297
  server.registerTool('gog_drive_du', {
298
298
  description: 'Summarize Drive folder sizes (disk-usage style) starting from a folder.',
299
299
  annotations: { readOnlyHint: true },
300
- inputSchema: {
300
+ inputSchema: z.object({
301
301
  parent: z.string().optional().describe('Folder ID to start from (default: root)'),
302
302
  depth: z.number().optional().describe('Depth for folder totals (default: 1)'),
303
303
  max: z.number().optional().describe('Max folders to return (0 = unlimited; default: 50)'),
@@ -305,7 +305,7 @@ export function registerExtraDriveTools(server: McpServer): void {
305
305
  order: z.enum(['asc', 'desc']).optional().describe('Sort order (default: desc)'),
306
306
  noAllDrives: z.boolean().optional().describe('My Drive only — exclude shared drives (shared drives are included by default)'),
307
307
  account: accountParam,
308
- },
308
+ }),
309
309
  }, async ({ parent, depth, max, sort, order, noAllDrives, account }) => {
310
310
  const args = ['drive', 'du'];
311
311
  if (parent) args.push(`--parent=${parent}`);
@@ -320,13 +320,13 @@ export function registerExtraDriveTools(server: McpServer): void {
320
320
  server.registerTool('gog_drive_tree', {
321
321
  description: 'Print a read-only folder tree starting from a folder.',
322
322
  annotations: { readOnlyHint: true },
323
- inputSchema: {
323
+ inputSchema: z.object({
324
324
  parent: z.string().optional().describe('Folder ID to start from (default: root)'),
325
325
  depth: z.number().optional().describe('Max depth (0 = unlimited; default: 2)'),
326
326
  max: z.number().optional().describe('Max items to return (0 = unlimited; default: 0)'),
327
327
  noAllDrives: z.boolean().optional().describe('My Drive only — exclude shared drives (shared drives are included by default)'),
328
328
  account: accountParam,
329
- },
329
+ }),
330
330
  }, async ({ parent, depth, max, noAllDrives, account }) => {
331
331
  const args = ['drive', 'tree'];
332
332
  if (parent) args.push(`--parent=${parent}`);
@@ -339,10 +339,10 @@ export function registerExtraDriveTools(server: McpServer): void {
339
339
  server.registerTool('gog_drive_changes_start_token', {
340
340
  description: 'Get a Drive changes start page token — the cursor you pass to gog_drive_changes_list to enumerate changes since this point.',
341
341
  annotations: { readOnlyHint: true },
342
- inputSchema: {
342
+ inputSchema: z.object({
343
343
  drive: z.string().optional().describe('Shared drive ID for a shared-drive change log'),
344
344
  account: accountParam,
345
- },
345
+ }),
346
346
  }, async ({ drive, account }) => {
347
347
  const args = ['drive', 'changes', 'start-token'];
348
348
  if (drive) args.push(`--drive=${drive}`);
@@ -352,7 +352,7 @@ export function registerExtraDriveTools(server: McpServer): void {
352
352
  server.registerTool('gog_drive_changes_list', {
353
353
  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.',
354
354
  annotations: { readOnlyHint: true },
355
- inputSchema: {
355
+ inputSchema: z.object({
356
356
  token: z.string().describe('Start page token or next page token'),
357
357
  max: z.number().optional().describe('Max results (default: 100)'),
358
358
  pageToken: pageTokenParam,
@@ -361,7 +361,7 @@ export function registerExtraDriveTools(server: McpServer): void {
361
361
  includeRemoved: z.boolean().optional().describe('Include removed changes'),
362
362
  drive: z.string().optional().describe('Shared drive ID for a shared-drive change log'),
363
363
  account: accountParam,
364
- },
364
+ }),
365
365
  }, async ({ token, max, pageToken, page, all, includeRemoved, drive, account }) => {
366
366
  const args = ['drive', 'changes', 'list', `--token=${token}`];
367
367
  if (max !== undefined) args.push(`--max=${max}`);
@@ -378,11 +378,11 @@ export function registerExtraDriveTools(server: McpServer): void {
378
378
  server.registerTool('gog_drive_revisions_list', {
379
379
  description: 'List a file\'s revision history — paged revision metadata plus provider export links for each revision.',
380
380
  annotations: { readOnlyHint: true },
381
- inputSchema: {
381
+ inputSchema: z.object({
382
382
  fileId: z.string().describe('File ID'),
383
383
  ...paginationParams,
384
384
  account: accountParam,
385
- },
385
+ }),
386
386
  }, async ({ fileId, max, page, all, account }) => {
387
387
  const args = ['drive', 'revisions', 'list', fileId];
388
388
  pushPaginationFlags(args, { max, page, all });
@@ -392,23 +392,23 @@ export function registerExtraDriveTools(server: McpServer): void {
392
392
  server.registerTool('gog_drive_revisions_get', {
393
393
  description: 'Get one revision of a Drive file by revision ID (metadata + export links). Find revision IDs with gog_drive_revisions_list.',
394
394
  annotations: { readOnlyHint: true },
395
- inputSchema: {
395
+ inputSchema: z.object({
396
396
  fileId: z.string().describe('File ID'),
397
397
  revisionId: z.string().describe('Revision ID'),
398
398
  account: accountParam,
399
- },
399
+ }),
400
400
  }, async ({ fileId, revisionId, account }) => {
401
401
  return runOrDiagnose(['drive', 'revisions', 'get', fileId, revisionId], { account });
402
402
  });
403
403
 
404
404
  server.registerTool('gog_drive_shortcut_create', {
405
405
  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.',
406
- inputSchema: {
406
+ inputSchema: z.object({
407
407
  targetId: z.string().describe('File or folder ID the shortcut points to'),
408
408
  parent: z.string().describe('Destination folder ID for the shortcut'),
409
409
  name: z.string().optional().describe('Shortcut name (default: the target\'s name)'),
410
410
  account: accountParam,
411
- },
411
+ }),
412
412
  }, async ({ targetId, parent, name, account }) => {
413
413
  const args = ['drive', 'shortcut', 'create', targetId, `--parent=${parent}`];
414
414
  if (name) args.push(`--name=${name}`);
@@ -418,7 +418,7 @@ export function registerExtraDriveTools(server: McpServer): void {
418
418
  server.registerTool('gog_drive_labels_list', {
419
419
  description: 'List Drive label schemas available to the account.',
420
420
  annotations: { readOnlyHint: true },
421
- inputSchema: {
421
+ inputSchema: z.object({
422
422
  language: z.string().optional().describe('BCP-47 language code'),
423
423
  view: z.enum(['LABEL_VIEW_BASIC', 'LABEL_VIEW_FULL']).optional().describe('Label view (default: LABEL_VIEW_BASIC)'),
424
424
  minimumRole: z.string().optional().describe('Minimum role filter (e.g. READER, APPLIER, ORGANIZER)'),
@@ -428,7 +428,7 @@ export function registerExtraDriveTools(server: McpServer): void {
428
428
  pageToken: pageTokenParam,
429
429
  page: pageAliasParam,
430
430
  account: accountParam,
431
- },
431
+ }),
432
432
  }, async ({ language, view, minimumRole, publishedOnly, adminAccess, max, pageToken, page, account }) => {
433
433
  const args = ['drive', 'labels', 'list'];
434
434
  if (max !== undefined) args.push(`--max=${max}`);
@@ -445,13 +445,13 @@ export function registerExtraDriveTools(server: McpServer): void {
445
445
  server.registerTool('gog_drive_labels_get', {
446
446
  description: 'Get a single Drive label schema by name (e.g. "labels/abc123").',
447
447
  annotations: { readOnlyHint: true },
448
- inputSchema: {
448
+ inputSchema: z.object({
449
449
  name: z.string().describe('Label schema name (e.g. labels/abc123)'),
450
450
  language: z.string().optional().describe('BCP-47 language code'),
451
451
  view: z.enum(['LABEL_VIEW_BASIC', 'LABEL_VIEW_FULL']).optional().describe('Label view (default: LABEL_VIEW_FULL)'),
452
452
  adminAccess: z.boolean().optional().describe('Use admin access for Workspace admin accounts'),
453
453
  account: accountParam,
454
- },
454
+ }),
455
455
  }, async ({ name, language, view, adminAccess, account }) => {
456
456
  const args = ['drive', 'labels', 'get', name];
457
457
  if (language) args.push(`--language=${language}`);
@@ -463,13 +463,13 @@ export function registerExtraDriveTools(server: McpServer): void {
463
463
  server.registerTool('gog_drive_labels_file_list', {
464
464
  description: 'List labels applied to a Drive file.',
465
465
  annotations: { readOnlyHint: true },
466
- inputSchema: {
466
+ inputSchema: z.object({
467
467
  fileId: z.string().describe('File ID'),
468
468
  max: z.number().optional().describe('Max results (default: 100)'),
469
469
  pageToken: pageTokenParam,
470
470
  page: pageAliasParam,
471
471
  account: accountParam,
472
- },
472
+ }),
473
473
  }, async ({ fileId, max, pageToken, page, account }) => {
474
474
  const args = ['drive', 'labels', 'file', 'list', fileId];
475
475
  if (max !== undefined) args.push(`--max=${max}`);
@@ -480,7 +480,7 @@ export function registerExtraDriveTools(server: McpServer): void {
480
480
 
481
481
  server.registerTool('gog_drive_labels_file_apply', {
482
482
  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.',
483
- inputSchema: {
483
+ inputSchema: z.object({
484
484
  fileId: z.string().describe('File ID'),
485
485
  labelId: z.string().describe('Label ID to apply'),
486
486
  text: z.array(z.string()).optional().describe('Text fields as fieldId=value (repeatable)'),
@@ -491,7 +491,7 @@ export function registerExtraDriveTools(server: McpServer): void {
491
491
  unset: z.array(z.string()).optional().describe('Field IDs to unset (repeatable)'),
492
492
  fieldsJson: z.string().optional().describe('Simple JSON object of fieldId to string/number/bool/string-array values'),
493
493
  account: accountParam,
494
- },
494
+ }),
495
495
  }, async ({ fileId, labelId, text, selection, integer, date, user, unset, fieldsJson, account }) => {
496
496
  const args = ['drive', 'labels', 'file', 'apply', fileId, labelId];
497
497
  if (text) for (const t of text) args.push(`--text=${t}`);
@@ -507,11 +507,11 @@ export function registerExtraDriveTools(server: McpServer): void {
507
507
  server.registerTool('gog_drive_labels_file_remove', {
508
508
  description: 'Remove a label from a Drive file.',
509
509
  annotations: { destructiveHint: true },
510
- inputSchema: {
510
+ inputSchema: z.object({
511
511
  fileId: z.string().describe('File ID'),
512
512
  labelId: z.string().describe('Label ID to remove'),
513
513
  account: accountParam,
514
- },
514
+ }),
515
515
  }, async ({ fileId, labelId, account }) => {
516
516
  return runOrDiagnose(['drive', 'labels', 'file', 'remove', fileId, labelId, '--force'], { account }); // gog gates this op; without --force the runner's --no-input makes it refuse
517
517
  });
@@ -519,7 +519,7 @@ export function registerExtraDriveTools(server: McpServer): void {
519
519
  server.registerTool('gog_drive_activity', {
520
520
  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.',
521
521
  annotations: { readOnlyHint: true },
522
- inputSchema: {
522
+ inputSchema: z.object({
523
523
  file: z.string().optional().describe('Drive file ID to query'),
524
524
  folder: z.string().optional().describe('Drive folder ID; includes descendants'),
525
525
  actions: z.string().optional().describe('Comma-separated action filters: edit,create,delete,move,rename,restore,comment,share,label,dlp,reference,settings'),
@@ -532,7 +532,7 @@ export function registerExtraDriveTools(server: McpServer): void {
532
532
  all: z.boolean().optional().describe('Fetch all pages'),
533
533
  consolidate: z.boolean().optional().describe('Use Drive Activity legacy consolidation strategy'),
534
534
  account: accountParam,
535
- },
535
+ }),
536
536
  }, async ({ file, folder, actions, from, to, filter, max, pageToken, page, all, consolidate, account }) => {
537
537
  const args = ['drive', 'activity', 'query'];
538
538
  if (file) args.push(`--file=${file}`);
@@ -552,7 +552,7 @@ export function registerExtraDriveTools(server: McpServer): void {
552
552
  server.registerTool('gog_drive_audit_sharing', {
553
553
  description: 'Audit Drive sharing without mutation — find public (anyone-with-link) or external permissions across a folder tree or a single file.',
554
554
  annotations: { readOnlyHint: true },
555
- inputSchema: {
555
+ inputSchema: z.object({
556
556
  file: z.string().optional().describe('Audit one file ID instead of a folder tree'),
557
557
  parent: z.string().optional().describe('Folder ID to scan (default: root)'),
558
558
  depth: z.number().optional().describe('Max folder depth (0 = unlimited; default: 2)'),
@@ -562,7 +562,7 @@ export function registerExtraDriveTools(server: McpServer): void {
562
562
  externalOnly: z.boolean().optional().describe('Only report external user/group/domain permissions'),
563
563
  noAllDrives: z.boolean().optional().describe('My Drive only — exclude shared drives (shared drives are included by default)'),
564
564
  account: accountParam,
565
- },
565
+ }),
566
566
  }, async ({ file, parent, depth, max, internalDomain, publicOnly, externalOnly, noAllDrives, account }) => {
567
567
  const args = ['drive', 'audit', 'sharing'];
568
568
  if (file) args.push(`--file=${file}`);
@@ -579,7 +579,7 @@ export function registerExtraDriveTools(server: McpServer): void {
579
579
  server.registerTool('gog_drive_audit_user', {
580
580
  description: 'Audit Drive sharing without mutation — find permissions granted to a specific user across a folder tree or a single file.',
581
581
  annotations: { readOnlyHint: true },
582
- inputSchema: {
582
+ inputSchema: z.object({
583
583
  user: z.string().describe('User email to audit permissions for'),
584
584
  file: z.string().optional().describe('Audit one file ID instead of a folder tree'),
585
585
  parent: z.string().optional().describe('Folder ID to scan (default: root)'),
@@ -587,7 +587,7 @@ export function registerExtraDriveTools(server: McpServer): void {
587
587
  max: z.number().optional().describe('Max files/folders to scan (0 = unlimited; default: 500)'),
588
588
  noAllDrives: z.boolean().optional().describe('My Drive only — exclude shared drives (shared drives are included by default)'),
589
589
  account: accountParam,
590
- },
590
+ }),
591
591
  }, async ({ user, file, parent, depth, max, noAllDrives, account }) => {
592
592
  const args = ['drive', 'audit', 'user', user];
593
593
  if (file) args.push(`--file=${file}`);
@@ -189,7 +189,7 @@ describe('gog_drive_upload', () => {
189
189
  // and it is reachable only through the gog_drive_run escape hatch — so the
190
190
  // description has to say so or the flag is unusable.
191
191
  it('description explains how to obtain the current version', async () => {
192
- const { McpServer } = await import('@modelcontextprotocol/sdk/server/mcp.js');
192
+ const { McpServer } = await import('@modelcontextprotocol/server');
193
193
  const server = new McpServer({ name: 'test', version: '0.0.0' });
194
194
  const configs = new Map<string, { description?: string }>();
195
195
  vi.spyOn(server, 'registerTool').mockImplementation((name, config) => {
@@ -803,15 +803,9 @@ describe('gog_drive_shortcut_create', () => {
803
803
  // conditional replace. The description has to say to convert it.
804
804
  describe('gog_drive_upload ifVersion description', () => {
805
805
  it('warns that the version reads back as a JSON string', async () => {
806
- const { McpServer } = await import('@modelcontextprotocol/sdk/server/mcp.js');
807
- const server = new McpServer({ name: 'test', version: '0.0.0' });
808
- const schemas = new Map<string, Record<string, { description?: string }>>();
809
- vi.spyOn(server, 'registerTool').mockImplementation((name, config) => {
810
- schemas.set(name, (config as { inputSchema: Record<string, { description?: string }> }).inputSchema);
811
- return undefined as never;
812
- });
813
- registerExtraDriveTools(server);
814
- const desc = schemas.get('gog_drive_upload')?.ifVersion?.description ?? '';
806
+ const listed = (await harness.client.listTools()).tools.find((tool) => tool.name === 'gog_drive_upload');
807
+ const properties = listed?.inputSchema.properties as Record<string, { description?: string }> | undefined;
808
+ const desc = properties?.ifVersion?.description ?? '';
815
809
  expect(desc).toMatch(/string/i);
816
810
  expect(desc).toMatch(/number/i);
817
811
  });
package/tsconfig.json CHANGED
@@ -5,5 +5,5 @@
5
5
  "rootDir": "../"
6
6
  },
7
7
  "include": ["src/**/*", "../gogcli-mcp/src/**/*"],
8
- "exclude": ["node_modules", "dist", "../gogcli-mcp/src/worker.ts"]
8
+ "exclude": ["node_modules", "dist"]
9
9
  }