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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/index.js +12 -531
- package/dist/lib.js +12 -531
- package/manifest.json +1 -145
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/runner.ts +12 -2
- package/src/tools/calendar.ts +0 -92
- package/src/tools/classroom.ts +2 -297
- package/src/tools/contacts.ts +1 -75
- package/src/tools/slides.ts +1 -108
- package/tests/helpers/extras-harness.ts +31 -0
- package/tests/runner.test.ts +63 -0
- package/tests/tools/calendar.test.ts +0 -137
- package/tests/tools/classroom.test.ts +0 -352
- package/tests/tools/contacts.test.ts +0 -100
- package/tests/tools/slides.test.ts +6 -278
package/src/tools/classroom.ts
CHANGED
|
@@ -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.
|
|
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'),
|
package/src/tools/contacts.ts
CHANGED
|
@@ -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
|
}
|
package/src/tools/slides.ts
CHANGED
|
@@ -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
|
+
}
|