gogcli-mcp 2.0.4 → 2.0.7

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.
@@ -37,88 +37,6 @@ export function registerClassroomTools(server: McpServer): void {
37
37
  return runOrDiagnose(['classroom', 'courses', 'get', courseId], { account });
38
38
  });
39
39
 
40
- server.registerTool('gog_classroom_courses_create', {
41
- description: 'Create a new Google Classroom course.',
42
- inputSchema: {
43
- name: z.string().describe('Course name'),
44
- owner: z.string().optional().describe('Owner user ID (default: "me")'),
45
- section: z.string().optional().describe('Section'),
46
- descriptionHeading: z.string().optional().describe('Description heading'),
47
- description: z.string().optional().describe('Description'),
48
- room: z.string().optional().describe('Room'),
49
- state: z.string().optional().describe('Course state: ACTIVE, ARCHIVED, PROVISIONED, DECLINED, SUSPENDED'),
50
- account: accountParam,
51
- },
52
- }, async ({ name, owner, section, descriptionHeading, description, room, state, account }) => {
53
- const args = ['classroom', 'courses', 'create', `--name=${name}`];
54
- if (owner) args.push(`--owner=${owner}`);
55
- if (section) args.push(`--section=${section}`);
56
- if (descriptionHeading) args.push(`--description-heading=${descriptionHeading}`);
57
- if (description) args.push(`--description=${description}`);
58
- if (room) args.push(`--room=${room}`);
59
- if (state) args.push(`--state=${state}`);
60
- return runOrDiagnose(args, { account });
61
- });
62
-
63
- server.registerTool('gog_classroom_courses_update', {
64
- description: 'Update an existing Google Classroom course.',
65
- annotations: { destructiveHint: true },
66
- inputSchema: {
67
- courseId: z.string().describe('Course ID'),
68
- name: z.string().optional().describe('Course name'),
69
- owner: z.string().optional().describe('Owner user ID'),
70
- section: z.string().optional().describe('Section'),
71
- descriptionHeading: z.string().optional().describe('Description heading'),
72
- description: z.string().optional().describe('Description'),
73
- room: z.string().optional().describe('Room'),
74
- state: z.string().optional().describe('Course state: ACTIVE, ARCHIVED, PROVISIONED, DECLINED, SUSPENDED'),
75
- account: accountParam,
76
- },
77
- }, async ({ courseId, name, owner, section, descriptionHeading, description, room, state, account }) => {
78
- const args = ['classroom', 'courses', 'update', courseId];
79
- if (name) args.push(`--name=${name}`);
80
- if (owner) args.push(`--owner=${owner}`);
81
- if (section) args.push(`--section=${section}`);
82
- if (descriptionHeading) args.push(`--description-heading=${descriptionHeading}`);
83
- if (description) args.push(`--description=${description}`);
84
- if (room) args.push(`--room=${room}`);
85
- if (state) args.push(`--state=${state}`);
86
- return runOrDiagnose(args, { account });
87
- });
88
-
89
- server.registerTool('gog_classroom_courses_delete', {
90
- description: 'Delete a Google Classroom course.',
91
- annotations: { destructiveHint: true },
92
- inputSchema: {
93
- courseId: z.string().describe('Course ID'),
94
- account: accountParam,
95
- },
96
- }, async ({ courseId, account }) => {
97
- return runOrDiagnose(['classroom', 'courses', 'delete', courseId], { account });
98
- });
99
-
100
- server.registerTool('gog_classroom_courses_archive', {
101
- description: 'Archive a Google Classroom course.',
102
- annotations: { destructiveHint: true },
103
- inputSchema: {
104
- courseId: z.string().describe('Course ID'),
105
- account: accountParam,
106
- },
107
- }, async ({ courseId, account }) => {
108
- return runOrDiagnose(['classroom', 'courses', 'archive', courseId], { account });
109
- });
110
-
111
- server.registerTool('gog_classroom_courses_unarchive', {
112
- description: 'Unarchive a Google Classroom course (restore to ACTIVE).',
113
- annotations: { destructiveHint: true },
114
- inputSchema: {
115
- courseId: z.string().describe('Course ID'),
116
- account: accountParam,
117
- },
118
- }, async ({ courseId, account }) => {
119
- return runOrDiagnose(['classroom', 'courses', 'unarchive', courseId], { account });
120
- });
121
-
122
40
  server.registerTool('gog_classroom_students_list', {
123
41
  description: 'List students enrolled in a Google Classroom course.',
124
42
  annotations: { readOnlyHint: true },
@@ -149,32 +67,6 @@ export function registerClassroomTools(server: McpServer): void {
149
67
  return runOrDiagnose(['classroom', 'students', 'get', courseId, userId], { account });
150
68
  });
151
69
 
152
- server.registerTool('gog_classroom_students_add', {
153
- description: 'Add a student to a Google Classroom course.',
154
- inputSchema: {
155
- courseId: z.string().describe('Course ID'),
156
- userId: z.string().describe('Student user ID (or "me")'),
157
- enrollmentCode: z.string().optional().describe('Enrollment code (required if adding self via code)'),
158
- account: accountParam,
159
- },
160
- }, async ({ courseId, userId, enrollmentCode, account }) => {
161
- const args = ['classroom', 'students', 'add', courseId, userId];
162
- if (enrollmentCode) args.push(`--enrollment-code=${enrollmentCode}`);
163
- return runOrDiagnose(args, { account });
164
- });
165
-
166
- server.registerTool('gog_classroom_students_remove', {
167
- description: 'Remove a student from a Google Classroom course.',
168
- annotations: { destructiveHint: true },
169
- inputSchema: {
170
- courseId: z.string().describe('Course ID'),
171
- userId: z.string().describe('Student user ID'),
172
- account: accountParam,
173
- },
174
- }, async ({ courseId, userId, account }) => {
175
- return runOrDiagnose(['classroom', 'students', 'remove', courseId, userId], { account });
176
- });
177
-
178
70
  server.registerTool('gog_classroom_teachers_list', {
179
71
  description: 'List teachers in a Google Classroom course.',
180
72
  annotations: { readOnlyHint: true },
@@ -205,29 +97,6 @@ export function registerClassroomTools(server: McpServer): void {
205
97
  return runOrDiagnose(['classroom', 'teachers', 'get', courseId, userId], { account });
206
98
  });
207
99
 
208
- server.registerTool('gog_classroom_teachers_add', {
209
- description: 'Add a teacher to a Google Classroom course.',
210
- inputSchema: {
211
- courseId: z.string().describe('Course ID'),
212
- userId: z.string().describe('Teacher user ID'),
213
- account: accountParam,
214
- },
215
- }, async ({ courseId, userId, account }) => {
216
- return runOrDiagnose(['classroom', 'teachers', 'add', courseId, userId], { account });
217
- });
218
-
219
- server.registerTool('gog_classroom_teachers_remove', {
220
- description: 'Remove a teacher from a Google Classroom course.',
221
- annotations: { destructiveHint: true },
222
- inputSchema: {
223
- courseId: z.string().describe('Course ID'),
224
- userId: z.string().describe('Teacher user ID'),
225
- account: accountParam,
226
- },
227
- }, async ({ courseId, userId, account }) => {
228
- return runOrDiagnose(['classroom', 'teachers', 'remove', courseId, userId], { account });
229
- });
230
-
231
100
  server.registerTool('gog_classroom_roster', {
232
101
  description: 'List the full roster (students and/or teachers) of a Google Classroom course. Omit both flags to return both groups.',
233
102
  annotations: { readOnlyHint: true },
@@ -288,81 +157,6 @@ export function registerClassroomTools(server: McpServer): void {
288
157
  return runOrDiagnose(['classroom', 'coursework', 'get', courseId, courseworkId], { account });
289
158
  });
290
159
 
291
- server.registerTool('gog_classroom_coursework_create', {
292
- description: 'Create a new coursework item (assignment, question, etc.) in a course.',
293
- inputSchema: {
294
- courseId: z.string().describe('Course ID'),
295
- title: z.string().describe('Coursework title'),
296
- description: z.string().optional().describe('Description'),
297
- type: z.string().optional().describe('Work type (ASSIGNMENT, SHORT_ANSWER_QUESTION, MULTIPLE_CHOICE_QUESTION). Default: ASSIGNMENT'),
298
- state: z.string().optional().describe('State: PUBLISHED or DRAFT'),
299
- maxPoints: z.number().optional().describe('Max points'),
300
- due: z.string().optional().describe('Due datetime (combined date+time)'),
301
- dueDate: z.string().optional().describe('Due date (YYYY-MM-DD)'),
302
- dueTime: z.string().optional().describe('Due time (HH:MM)'),
303
- scheduled: z.string().optional().describe('Scheduled publish time'),
304
- topic: z.string().optional().describe('Topic ID'),
305
- account: accountParam,
306
- },
307
- }, async ({ courseId, title, description, type, state, maxPoints, due, dueDate, dueTime, scheduled, topic, account }) => {
308
- const args = ['classroom', 'coursework', 'create', courseId, `--title=${title}`];
309
- if (description) args.push(`--description=${description}`);
310
- if (type) args.push(`--type=${type}`);
311
- if (state) args.push(`--state=${state}`);
312
- if (maxPoints !== undefined) args.push(`--max-points=${maxPoints}`);
313
- if (due) args.push(`--due=${due}`);
314
- if (dueDate) args.push(`--due-date=${dueDate}`);
315
- if (dueTime) args.push(`--due-time=${dueTime}`);
316
- if (scheduled) args.push(`--scheduled=${scheduled}`);
317
- if (topic) args.push(`--topic=${topic}`);
318
- return runOrDiagnose(args, { account });
319
- });
320
-
321
- server.registerTool('gog_classroom_coursework_update', {
322
- description: 'Update an existing coursework item.',
323
- annotations: { destructiveHint: true },
324
- inputSchema: {
325
- courseId: z.string().describe('Course ID'),
326
- courseworkId: z.string().describe('Coursework ID'),
327
- title: z.string().optional().describe('New title'),
328
- description: z.string().optional().describe('New description'),
329
- type: z.string().optional().describe('Work type'),
330
- state: z.string().optional().describe('State: PUBLISHED or DRAFT'),
331
- maxPoints: z.number().optional().describe('Max points'),
332
- due: z.string().optional().describe('Due datetime'),
333
- dueDate: z.string().optional().describe('Due date (YYYY-MM-DD)'),
334
- dueTime: z.string().optional().describe('Due time (HH:MM)'),
335
- scheduled: z.string().optional().describe('Scheduled publish time'),
336
- topic: z.string().optional().describe('Topic ID'),
337
- account: accountParam,
338
- },
339
- }, async ({ courseId, courseworkId, title, description, type, state, maxPoints, due, dueDate, dueTime, scheduled, topic, account }) => {
340
- const args = ['classroom', 'coursework', 'update', courseId, courseworkId];
341
- if (title) args.push(`--title=${title}`);
342
- if (description) args.push(`--description=${description}`);
343
- if (type) args.push(`--type=${type}`);
344
- if (state) args.push(`--state=${state}`);
345
- if (maxPoints !== undefined) args.push(`--max-points=${maxPoints}`);
346
- if (due) args.push(`--due=${due}`);
347
- if (dueDate) args.push(`--due-date=${dueDate}`);
348
- if (dueTime) args.push(`--due-time=${dueTime}`);
349
- if (scheduled) args.push(`--scheduled=${scheduled}`);
350
- if (topic) args.push(`--topic=${topic}`);
351
- return runOrDiagnose(args, { account });
352
- });
353
-
354
- server.registerTool('gog_classroom_coursework_delete', {
355
- description: 'Delete a coursework item.',
356
- annotations: { destructiveHint: true },
357
- inputSchema: {
358
- courseId: z.string().describe('Course ID'),
359
- courseworkId: z.string().describe('Coursework ID'),
360
- account: accountParam,
361
- },
362
- }, async ({ courseId, courseworkId, account }) => {
363
- return runOrDiagnose(['classroom', 'coursework', 'delete', courseId, courseworkId], { account });
364
- });
365
-
366
160
  server.registerTool('gog_classroom_submissions_list', {
367
161
  description: 'List student submissions for a coursework item.',
368
162
  annotations: { readOnlyHint: true },
@@ -497,7 +291,7 @@ export function registerClassroomTools(server: McpServer): void {
497
291
  inputSchema: {
498
292
  courseId: z.string().describe('Course ID'),
499
293
  text: z.string().describe('Announcement text'),
500
- state: z.string().optional().describe('State: PUBLISHED or DRAFT'),
294
+ state: z.enum(['PUBLISHED', 'DRAFT']).optional().describe('State'),
501
295
  scheduled: z.string().optional().describe('Scheduled publish time'),
502
296
  account: accountParam,
503
297
  },
@@ -508,37 +302,6 @@ export function registerClassroomTools(server: McpServer): void {
508
302
  return runOrDiagnose(args, { account });
509
303
  });
510
304
 
511
- server.registerTool('gog_classroom_announcements_update', {
512
- description: 'Update an existing announcement.',
513
- annotations: { destructiveHint: true },
514
- inputSchema: {
515
- courseId: z.string().describe('Course ID'),
516
- announcementId: z.string().describe('Announcement ID'),
517
- text: z.string().optional().describe('New text'),
518
- state: z.string().optional().describe('State: PUBLISHED or DRAFT'),
519
- scheduled: z.string().optional().describe('Scheduled publish time'),
520
- account: accountParam,
521
- },
522
- }, async ({ courseId, announcementId, text, state, scheduled, account }) => {
523
- const args = ['classroom', 'announcements', 'update', courseId, announcementId];
524
- if (text) args.push(`--text=${text}`);
525
- if (state) args.push(`--state=${state}`);
526
- if (scheduled) args.push(`--scheduled=${scheduled}`);
527
- return runOrDiagnose(args, { account });
528
- });
529
-
530
- server.registerTool('gog_classroom_announcements_delete', {
531
- description: 'Delete an announcement.',
532
- annotations: { destructiveHint: true },
533
- inputSchema: {
534
- courseId: z.string().describe('Course ID'),
535
- announcementId: z.string().describe('Announcement ID'),
536
- account: accountParam,
537
- },
538
- }, async ({ courseId, announcementId, account }) => {
539
- return runOrDiagnose(['classroom', 'announcements', 'delete', courseId, announcementId], { account });
540
- });
541
-
542
305
  server.registerTool('gog_classroom_topics_list', {
543
306
  description: 'List topics in a Google Classroom course.',
544
307
  annotations: { readOnlyHint: true },
@@ -569,42 +332,6 @@ export function registerClassroomTools(server: McpServer): void {
569
332
  return runOrDiagnose(['classroom', 'topics', 'get', courseId, topicId], { account });
570
333
  });
571
334
 
572
- server.registerTool('gog_classroom_topics_create', {
573
- description: 'Create a topic in a Google Classroom course.',
574
- inputSchema: {
575
- courseId: z.string().describe('Course ID'),
576
- name: z.string().describe('Topic name'),
577
- account: accountParam,
578
- },
579
- }, async ({ courseId, name, account }) => {
580
- return runOrDiagnose(['classroom', 'topics', 'create', courseId, `--name=${name}`], { account });
581
- });
582
-
583
- server.registerTool('gog_classroom_topics_update', {
584
- description: 'Rename an existing topic.',
585
- annotations: { destructiveHint: true },
586
- inputSchema: {
587
- courseId: z.string().describe('Course ID'),
588
- topicId: z.string().describe('Topic ID'),
589
- name: z.string().describe('New topic name'),
590
- account: accountParam,
591
- },
592
- }, async ({ courseId, topicId, name, account }) => {
593
- return runOrDiagnose(['classroom', 'topics', 'update', courseId, topicId, `--name=${name}`], { account });
594
- });
595
-
596
- server.registerTool('gog_classroom_topics_delete', {
597
- description: 'Delete a topic.',
598
- annotations: { destructiveHint: true },
599
- inputSchema: {
600
- courseId: z.string().describe('Course ID'),
601
- topicId: z.string().describe('Topic ID'),
602
- account: accountParam,
603
- },
604
- }, async ({ courseId, topicId, account }) => {
605
- return runOrDiagnose(['classroom', 'topics', 'delete', courseId, topicId], { account });
606
- });
607
-
608
335
  server.registerTool('gog_classroom_invitations_list', {
609
336
  description: 'List Google Classroom invitations.',
610
337
  annotations: { readOnlyHint: true },
@@ -637,18 +364,6 @@ export function registerClassroomTools(server: McpServer): void {
637
364
  return runOrDiagnose(['classroom', 'invitations', 'get', invitationId], { account });
638
365
  });
639
366
 
640
- server.registerTool('gog_classroom_invitations_create', {
641
- description: 'Create an invitation to a Google Classroom course.',
642
- inputSchema: {
643
- courseId: z.string().describe('Course ID'),
644
- userId: z.string().describe('User ID to invite'),
645
- role: z.enum(['STUDENT', 'TEACHER', 'OWNER']).describe('Role for the invited user'),
646
- account: accountParam,
647
- },
648
- }, async ({ courseId, userId, role, account }) => {
649
- return runOrDiagnose(['classroom', 'invitations', 'create', courseId, userId, `--role=${role}`], { account });
650
- });
651
-
652
367
  server.registerTool('gog_classroom_invitations_accept', {
653
368
  description: 'Accept a Google Classroom invitation.',
654
369
  inputSchema: {
@@ -659,17 +374,6 @@ export function registerClassroomTools(server: McpServer): void {
659
374
  return runOrDiagnose(['classroom', 'invitations', 'accept', invitationId], { account });
660
375
  });
661
376
 
662
- server.registerTool('gog_classroom_invitations_delete', {
663
- description: 'Delete (revoke) a Google Classroom invitation.',
664
- annotations: { destructiveHint: true },
665
- inputSchema: {
666
- invitationId: z.string().describe('Invitation ID'),
667
- account: accountParam,
668
- },
669
- }, async ({ invitationId, account }) => {
670
- return runOrDiagnose(['classroom', 'invitations', 'delete', invitationId], { account });
671
- });
672
-
673
377
  server.registerTool('gog_classroom_profile_get', {
674
378
  description: 'Get a Google Classroom user profile. Omit userId to fetch the authenticated user.',
675
379
  annotations: { readOnlyHint: true },
@@ -685,6 +389,7 @@ export function registerClassroomTools(server: McpServer): void {
685
389
 
686
390
  server.registerTool('gog_classroom_run', {
687
391
  description: 'Run any gog classroom subcommand not covered by the other tools (guardians, guardian-invitations, materials, coursework assignees, announcement assignees, etc.). Run `gog classroom --help` for the full list, or `gog classroom <subcommand> --help` for flags.',
392
+ annotations: { destructiveHint: true },
688
393
  inputSchema: {
689
394
  subcommand: z.string().describe('The gog classroom subcommand to run, e.g. "guardians", "materials", "guardian-invitations"'),
690
395
  args: z.array(z.string()).describe('Additional positional args and flags'),
@@ -4,7 +4,7 @@ import { accountParam, runOrDiagnose } from './utils.js';
4
4
 
5
5
  export function registerContactsTools(server: McpServer): void {
6
6
  server.registerTool('gog_contacts_search', {
7
- description: 'Search Google Contacts by name, email, or phone.',
7
+ description: 'Search personal Google Contacts by name, email, or phone. For searching the Workspace directory (internal users not in your personal contacts), use gog_people_search from gogcli-mcp-contacts.',
8
8
  annotations: { readOnlyHint: true },
9
9
  inputSchema: {
10
10
  query: z.string().describe('Search query (name, email, or phone)'),
@@ -68,78 +68,4 @@ export function registerContactsTools(server: McpServer): void {
68
68
  }, async ({ subcommand, args, account }) => {
69
69
  return runOrDiagnose(['contacts', subcommand, ...args], { account });
70
70
  });
71
-
72
- // ─── People API tools ──────────────────────────────────────────
73
- // Folded into the contacts wrapper because People is the richer API behind
74
- // Google Contacts (Workspace directory search, profile fields, relations).
75
-
76
- server.registerTool('gog_people_me', {
77
- description: 'Show your own People profile (people/me).',
78
- annotations: { readOnlyHint: true },
79
- inputSchema: {
80
- account: accountParam,
81
- },
82
- }, async ({ account }) => {
83
- return runOrDiagnose(['people', 'me'], { account });
84
- });
85
-
86
- server.registerTool('gog_people_get', {
87
- description: 'Get a People profile by resource name.',
88
- annotations: { readOnlyHint: true },
89
- inputSchema: {
90
- userId: z.string().describe('Person resource name (people/...) or email'),
91
- account: accountParam,
92
- },
93
- }, async ({ userId, account }) => {
94
- return runOrDiagnose(['people', 'get', userId], { account });
95
- });
96
-
97
- server.registerTool('gog_people_search', {
98
- description: 'Search the Google Workspace directory (covers internal users, unlike contacts search which is limited to your personal contacts).',
99
- annotations: { readOnlyHint: true },
100
- inputSchema: {
101
- query: z.string().describe('Search query (name, email, etc.)'),
102
- max: z.number().optional().describe('Max results (default: 50)'),
103
- page: z.string().optional().describe('Page token'),
104
- all: z.boolean().optional().describe('Fetch all pages'),
105
- account: accountParam,
106
- },
107
- }, async ({ query, max, page, all, account }) => {
108
- const args = ['people', 'search', query];
109
- if (max !== undefined) args.push(`--max=${max}`);
110
- if (page) args.push(`--page=${page}`);
111
- if (all) args.push('--all');
112
- return runOrDiagnose(args, { account });
113
- });
114
-
115
- server.registerTool('gog_people_relations', {
116
- description: 'Get relations (manager, reports, etc.) for a user. Defaults to self when userId is omitted.',
117
- annotations: { readOnlyHint: true },
118
- inputSchema: {
119
- userId: z.string().optional().describe('Person resource name (defaults to self when omitted)'),
120
- type: z.string().optional().describe('Filter to a specific relation type (e.g. "manager")'),
121
- account: accountParam,
122
- },
123
- }, async ({ userId, type, account }) => {
124
- const args = ['people', 'relations'];
125
- if (userId) args.push(userId);
126
- if (type) args.push(`--type=${type}`);
127
- return runOrDiagnose(args, { account });
128
- });
129
-
130
- server.registerTool('gog_people_raw', {
131
- description: 'Dump the raw People API response as JSON (lossless; for scripting and LLM consumption).',
132
- annotations: { readOnlyHint: true },
133
- inputSchema: {
134
- userId: z.string().describe('Person resource name (people/...) or email'),
135
- personFields: z.string().optional().describe('People API personFields mask (default: broad set)'),
136
- pretty: z.boolean().optional().describe('Pretty-print JSON (default: compact single-line)'),
137
- account: accountParam,
138
- },
139
- }, async ({ userId, personFields, pretty, account }) => {
140
- const args = ['people', 'raw', userId];
141
- if (personFields) args.push(`--person-fields=${personFields}`);
142
- if (pretty) args.push('--pretty');
143
- return runOrDiagnose(args, { account });
144
- });
145
71
  }
@@ -45,49 +45,6 @@ export function registerSlidesTools(server: McpServer): void {
45
45
  return runOrDiagnose(args, { account });
46
46
  });
47
47
 
48
- server.registerTool('gog_slides_create_from_markdown', {
49
- description: 'Create a new Google Slides presentation from markdown content (inline or from a file).',
50
- inputSchema: {
51
- title: z.string().describe('Presentation title'),
52
- content: z.string().optional().describe('Inline markdown content'),
53
- contentFile: z.string().optional().describe('Path to a markdown file'),
54
- parent: z.string().optional().describe('Destination folder ID'),
55
- debug: z.boolean().optional().describe('Enable debug output'),
56
- account: accountParam,
57
- },
58
- }, async ({ title, content, contentFile, parent, debug, account }) => {
59
- const args = ['slides', 'create-from-markdown', title];
60
- if (content) args.push(`--content=${content}`);
61
- if (contentFile) args.push(`--content-file=${contentFile}`);
62
- if (parent) args.push(`--parent=${parent}`);
63
- if (debug) args.push('--debug');
64
- return runOrDiagnose(args, { account });
65
- });
66
-
67
- server.registerTool('gog_slides_create_from_template', {
68
- description: 'Create a new Google Slides presentation from a template, with optional placeholder replacements.',
69
- inputSchema: {
70
- templateId: z.string().describe('Template presentation ID'),
71
- title: z.string().describe('New presentation title'),
72
- replacements: z.record(z.string(), z.string()).optional().describe('Placeholder replacements as a key/value object (emitted as --replace=k=v for each entry)'),
73
- replacementsFile: z.string().optional().describe('Path to a JSON file containing replacements'),
74
- parent: z.string().optional().describe('Destination folder ID'),
75
- exact: z.boolean().optional().describe('Require exact placeholder matches'),
76
- account: accountParam,
77
- },
78
- }, async ({ templateId, title, replacements, replacementsFile, parent, exact, account }) => {
79
- const args = ['slides', 'create-from-template', templateId, title];
80
- if (replacements) {
81
- for (const [k, v] of Object.entries(replacements)) {
82
- args.push(`--replace=${k}=${v}`);
83
- }
84
- }
85
- if (replacementsFile) args.push(`--replacements=${replacementsFile}`);
86
- if (parent) args.push(`--parent=${parent}`);
87
- if (exact) args.push('--exact');
88
- return runOrDiagnose(args, { account });
89
- });
90
-
91
48
  server.registerTool('gog_slides_copy', {
92
49
  description: 'Copy a Google Slides presentation to a new presentation with the given title.',
93
50
  inputSchema: {
@@ -102,24 +59,6 @@ export function registerSlidesTools(server: McpServer): void {
102
59
  return runOrDiagnose(args, { account });
103
60
  });
104
61
 
105
- server.registerTool('gog_slides_add_slide', {
106
- description: 'Add a new slide to a presentation from a local image, with optional speaker notes.',
107
- inputSchema: {
108
- presentationId: z.string().describe('Presentation ID'),
109
- image: z.string().describe('Path to the local image file'),
110
- notes: z.string().optional().describe('Speaker notes text'),
111
- notesFile: z.string().optional().describe('Path to a file containing speaker notes'),
112
- before: z.string().optional().describe('Insert before this slide ID (default: append at end)'),
113
- account: accountParam,
114
- },
115
- }, async ({ presentationId, image, notes, notesFile, before, account }) => {
116
- const args = ['slides', 'add-slide', presentationId, image];
117
- if (notes) args.push(`--notes=${notes}`);
118
- if (notesFile) args.push(`--notes-file=${notesFile}`);
119
- if (before) args.push(`--before=${before}`);
120
- return runOrDiagnose(args, { account });
121
- });
122
-
123
62
  server.registerTool('gog_slides_list_slides', {
124
63
  description: 'List slides in a Google Slides presentation.',
125
64
  annotations: { readOnlyHint: true },
@@ -131,18 +70,6 @@ export function registerSlidesTools(server: McpServer): void {
131
70
  return runOrDiagnose(['slides', 'list-slides', presentationId], { account });
132
71
  });
133
72
 
134
- server.registerTool('gog_slides_delete_slide', {
135
- description: 'Delete a slide from a Google Slides presentation.',
136
- annotations: { destructiveHint: true },
137
- inputSchema: {
138
- presentationId: z.string().describe('Presentation ID'),
139
- slideId: z.string().describe('Slide ID to delete'),
140
- account: accountParam,
141
- },
142
- }, async ({ presentationId, slideId, account }) => {
143
- return runOrDiagnose(['slides', 'delete-slide', presentationId, slideId], { account });
144
- });
145
-
146
73
  server.registerTool('gog_slides_read_slide', {
147
74
  description: 'Read the content of a slide (text, shapes, speaker notes).',
148
75
  annotations: { readOnlyHint: true },
@@ -155,43 +82,9 @@ export function registerSlidesTools(server: McpServer): void {
155
82
  return runOrDiagnose(['slides', 'read-slide', presentationId, slideId], { account });
156
83
  });
157
84
 
158
- server.registerTool('gog_slides_update_notes', {
159
- description: 'Update the speaker notes on a slide (inline text or from a file).',
160
- annotations: { destructiveHint: true },
161
- inputSchema: {
162
- presentationId: z.string().describe('Presentation ID'),
163
- slideId: z.string().describe('Slide ID'),
164
- notes: z.string().optional().describe('New speaker notes text'),
165
- notesFile: z.string().optional().describe('Path to a file containing new speaker notes'),
166
- account: accountParam,
167
- },
168
- }, async ({ presentationId, slideId, notes, notesFile, account }) => {
169
- const args = ['slides', 'update-notes', presentationId, slideId];
170
- if (notes) args.push(`--notes=${notes}`);
171
- if (notesFile) args.push(`--notes-file=${notesFile}`);
172
- return runOrDiagnose(args, { account });
173
- });
174
-
175
- server.registerTool('gog_slides_replace_slide', {
176
- description: 'Replace the image content of an existing slide, with optional speaker notes.',
177
- annotations: { destructiveHint: true },
178
- inputSchema: {
179
- presentationId: z.string().describe('Presentation ID'),
180
- slideId: z.string().describe('Slide ID to replace'),
181
- image: z.string().describe('Path to the new local image file'),
182
- notes: z.string().optional().describe('Speaker notes text'),
183
- notesFile: z.string().optional().describe('Path to a file containing speaker notes'),
184
- account: accountParam,
185
- },
186
- }, async ({ presentationId, slideId, image, notes, notesFile, account }) => {
187
- const args = ['slides', 'replace-slide', presentationId, slideId, image];
188
- if (notes) args.push(`--notes=${notes}`);
189
- if (notesFile) args.push(`--notes-file=${notesFile}`);
190
- return runOrDiagnose(args, { account });
191
- });
192
-
193
85
  server.registerTool('gog_slides_run', {
194
86
  description: 'Run any gog slides subcommand not covered by the other tools. Run `gog slides --help` for the full list of subcommands, or `gog slides <subcommand> --help` for flags on a specific subcommand.',
87
+ annotations: { destructiveHint: true },
195
88
  inputSchema: {
196
89
  subcommand: z.string().describe('The gog slides subcommand to run'),
197
90
  args: z.array(z.string()).describe('Additional positional args and flags'),
@@ -0,0 +1,31 @@
1
+ // Shared test harness for `*-extra.ts` registrars across sub-packages.
2
+ //
3
+ // Each sub-package's extras test file follows the same shape: mock the
4
+ // `runOrDiagnose` export from `gogcli-mcp/lib`, register the extras tools onto
5
+ // a stub `McpServer`, capture each tool's handler into a Map, and exercise the
6
+ // handlers with sample inputs. The `vi.mock(...)` call must stay in the
7
+ // caller's test file (vitest hoists it at the module scope), but the
8
+ // boilerplate around it can live here.
9
+ import { vi } from 'vitest';
10
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
11
+
12
+ export type ToolHandler = (
13
+ args: Record<string, unknown>,
14
+ ) => Promise<{ content: Array<{ type: string; text: string }> }>;
15
+
16
+ export function toText(text: string): { content: Array<{ type: string; text: string }> } {
17
+ return { content: [{ type: 'text', text }] };
18
+ }
19
+
20
+ export function setupExtrasHandlers(
21
+ register: (server: McpServer) => void,
22
+ ): Map<string, ToolHandler> {
23
+ const server = new McpServer({ name: 'test', version: '0.0.0' });
24
+ const handlers = new Map<string, ToolHandler>();
25
+ vi.spyOn(server, 'registerTool').mockImplementation((name, _config, cb) => {
26
+ handlers.set(name, cb as ToolHandler);
27
+ return undefined as never;
28
+ });
29
+ register(server);
30
+ return handlers;
31
+ }