gogcli-mcp 2.0.0 → 2.0.2

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.
@@ -0,0 +1,696 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { z } from 'zod';
3
+ import { accountParam, runOrDiagnose } from './utils.js';
4
+
5
+ export function registerClassroomTools(server: McpServer): void {
6
+ server.registerTool('gog_classroom_courses_list', {
7
+ description: 'List Google Classroom courses.',
8
+ annotations: { readOnlyHint: true },
9
+ inputSchema: {
10
+ state: z.string().optional().describe('Comma-separated course states (ACTIVE, ARCHIVED, PROVISIONED, DECLINED, SUSPENDED)'),
11
+ teacher: z.string().optional().describe('Filter by teacher user ID'),
12
+ student: z.string().optional().describe('Filter by student user ID'),
13
+ max: z.number().optional().describe('Max results per page'),
14
+ page: z.string().optional().describe('Page token for pagination'),
15
+ all: z.boolean().optional().describe('Fetch all pages'),
16
+ account: accountParam,
17
+ },
18
+ }, async ({ state, teacher, student, max, page, all, account }) => {
19
+ const args = ['classroom', 'courses', 'list'];
20
+ if (state) args.push(`--state=${state}`);
21
+ if (teacher) args.push(`--teacher=${teacher}`);
22
+ if (student) args.push(`--student=${student}`);
23
+ if (max !== undefined) args.push(`--max=${max}`);
24
+ if (page) args.push(`--page=${page}`);
25
+ if (all) args.push('--all');
26
+ return runOrDiagnose(args, { account });
27
+ });
28
+
29
+ server.registerTool('gog_classroom_courses_get', {
30
+ description: 'Get a single Google Classroom course by ID.',
31
+ annotations: { readOnlyHint: true },
32
+ inputSchema: {
33
+ courseId: z.string().describe('Course ID'),
34
+ account: accountParam,
35
+ },
36
+ }, async ({ courseId, account }) => {
37
+ return runOrDiagnose(['classroom', 'courses', 'get', courseId], { account });
38
+ });
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
+ server.registerTool('gog_classroom_students_list', {
123
+ description: 'List students enrolled in a Google Classroom course.',
124
+ annotations: { readOnlyHint: true },
125
+ inputSchema: {
126
+ courseId: z.string().describe('Course ID'),
127
+ max: z.number().optional().describe('Max results per page'),
128
+ page: z.string().optional().describe('Page token'),
129
+ all: z.boolean().optional().describe('Fetch all pages'),
130
+ account: accountParam,
131
+ },
132
+ }, async ({ courseId, max, page, all, account }) => {
133
+ const args = ['classroom', 'students', 'list', courseId];
134
+ if (max !== undefined) args.push(`--max=${max}`);
135
+ if (page) args.push(`--page=${page}`);
136
+ if (all) args.push('--all');
137
+ return runOrDiagnose(args, { account });
138
+ });
139
+
140
+ server.registerTool('gog_classroom_students_get', {
141
+ description: 'Get a specific student enrolled in a course.',
142
+ annotations: { readOnlyHint: true },
143
+ inputSchema: {
144
+ courseId: z.string().describe('Course ID'),
145
+ userId: z.string().describe('Student user ID'),
146
+ account: accountParam,
147
+ },
148
+ }, async ({ courseId, userId, account }) => {
149
+ return runOrDiagnose(['classroom', 'students', 'get', courseId, userId], { account });
150
+ });
151
+
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
+ server.registerTool('gog_classroom_teachers_list', {
179
+ description: 'List teachers in a Google Classroom course.',
180
+ annotations: { readOnlyHint: true },
181
+ inputSchema: {
182
+ courseId: z.string().describe('Course ID'),
183
+ max: z.number().optional().describe('Max results per page'),
184
+ page: z.string().optional().describe('Page token'),
185
+ all: z.boolean().optional().describe('Fetch all pages'),
186
+ account: accountParam,
187
+ },
188
+ }, async ({ courseId, max, page, all, account }) => {
189
+ const args = ['classroom', 'teachers', 'list', courseId];
190
+ if (max !== undefined) args.push(`--max=${max}`);
191
+ if (page) args.push(`--page=${page}`);
192
+ if (all) args.push('--all');
193
+ return runOrDiagnose(args, { account });
194
+ });
195
+
196
+ server.registerTool('gog_classroom_teachers_get', {
197
+ description: 'Get a specific teacher in a course.',
198
+ annotations: { readOnlyHint: true },
199
+ inputSchema: {
200
+ courseId: z.string().describe('Course ID'),
201
+ userId: z.string().describe('Teacher user ID'),
202
+ account: accountParam,
203
+ },
204
+ }, async ({ courseId, userId, account }) => {
205
+ return runOrDiagnose(['classroom', 'teachers', 'get', courseId, userId], { account });
206
+ });
207
+
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
+ server.registerTool('gog_classroom_roster', {
232
+ description: 'List the full roster (students and/or teachers) of a Google Classroom course. Omit both flags to return both groups.',
233
+ annotations: { readOnlyHint: true },
234
+ inputSchema: {
235
+ courseId: z.string().describe('Course ID'),
236
+ students: z.boolean().optional().describe('Include students only'),
237
+ teachers: z.boolean().optional().describe('Include teachers only'),
238
+ max: z.number().optional().describe('Max results per page'),
239
+ page: z.string().optional().describe('Page token'),
240
+ all: z.boolean().optional().describe('Fetch all pages'),
241
+ account: accountParam,
242
+ },
243
+ }, async ({ courseId, students, teachers, max, page, all, account }) => {
244
+ const args = ['classroom', 'roster', courseId];
245
+ if (students) args.push('--students');
246
+ if (teachers) args.push('--teachers');
247
+ if (max !== undefined) args.push(`--max=${max}`);
248
+ if (page) args.push(`--page=${page}`);
249
+ if (all) args.push('--all');
250
+ return runOrDiagnose(args, { account });
251
+ });
252
+
253
+ server.registerTool('gog_classroom_coursework_list', {
254
+ description: 'List coursework (assignments, questions, materials) for a course.',
255
+ annotations: { readOnlyHint: true },
256
+ inputSchema: {
257
+ courseId: z.string().describe('Course ID'),
258
+ state: z.string().optional().describe('Filter by coursework state (PUBLISHED, DRAFT, DELETED)'),
259
+ topic: z.string().optional().describe('Filter by topic ID'),
260
+ orderBy: z.string().optional().describe('Sort order (e.g. "updateTime desc")'),
261
+ max: z.number().optional().describe('Max results per page'),
262
+ page: z.string().optional().describe('Page token'),
263
+ all: z.boolean().optional().describe('Fetch all pages'),
264
+ scanPages: z.number().optional().describe('Max pages to scan when filtering'),
265
+ account: accountParam,
266
+ },
267
+ }, async ({ courseId, state, topic, orderBy, max, page, all, scanPages, account }) => {
268
+ const args = ['classroom', 'coursework', 'list', courseId];
269
+ if (state) args.push(`--state=${state}`);
270
+ if (topic) args.push(`--topic=${topic}`);
271
+ if (orderBy) args.push(`--order-by=${orderBy}`);
272
+ if (max !== undefined) args.push(`--max=${max}`);
273
+ if (page) args.push(`--page=${page}`);
274
+ if (all) args.push('--all');
275
+ if (scanPages !== undefined) args.push(`--scan-pages=${scanPages}`);
276
+ return runOrDiagnose(args, { account });
277
+ });
278
+
279
+ server.registerTool('gog_classroom_coursework_get', {
280
+ description: 'Get a single coursework item by ID.',
281
+ annotations: { readOnlyHint: true },
282
+ inputSchema: {
283
+ courseId: z.string().describe('Course ID'),
284
+ courseworkId: z.string().describe('Coursework ID'),
285
+ account: accountParam,
286
+ },
287
+ }, async ({ courseId, courseworkId, account }) => {
288
+ return runOrDiagnose(['classroom', 'coursework', 'get', courseId, courseworkId], { account });
289
+ });
290
+
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
+ server.registerTool('gog_classroom_submissions_list', {
367
+ description: 'List student submissions for a coursework item.',
368
+ annotations: { readOnlyHint: true },
369
+ inputSchema: {
370
+ courseId: z.string().describe('Course ID'),
371
+ courseworkId: z.string().describe('Coursework ID'),
372
+ state: z.string().optional().describe('Filter by submission state'),
373
+ late: z.enum(['late', 'not-late']).optional().describe('Filter by late status'),
374
+ user: z.string().optional().describe('Filter by student user ID'),
375
+ max: z.number().optional().describe('Max results per page'),
376
+ page: z.string().optional().describe('Page token'),
377
+ all: z.boolean().optional().describe('Fetch all pages'),
378
+ account: accountParam,
379
+ },
380
+ }, async ({ courseId, courseworkId, state, late, user, max, page, all, account }) => {
381
+ const args = ['classroom', 'submissions', 'list', courseId, courseworkId];
382
+ if (state) args.push(`--state=${state}`);
383
+ if (late) args.push(`--late=${late}`);
384
+ if (user) args.push(`--user=${user}`);
385
+ if (max !== undefined) args.push(`--max=${max}`);
386
+ if (page) args.push(`--page=${page}`);
387
+ if (all) args.push('--all');
388
+ return runOrDiagnose(args, { account });
389
+ });
390
+
391
+ server.registerTool('gog_classroom_submissions_get', {
392
+ description: 'Get a single submission by ID.',
393
+ annotations: { readOnlyHint: true },
394
+ inputSchema: {
395
+ courseId: z.string().describe('Course ID'),
396
+ courseworkId: z.string().describe('Coursework ID'),
397
+ submissionId: z.string().describe('Submission ID'),
398
+ account: accountParam,
399
+ },
400
+ }, async ({ courseId, courseworkId, submissionId, account }) => {
401
+ return runOrDiagnose(['classroom', 'submissions', 'get', courseId, courseworkId, submissionId], { account });
402
+ });
403
+
404
+ server.registerTool('gog_classroom_submissions_grade', {
405
+ description: 'Grade a student submission. Set draft and/or assigned grade values.',
406
+ annotations: { destructiveHint: true },
407
+ inputSchema: {
408
+ courseId: z.string().describe('Course ID'),
409
+ courseworkId: z.string().describe('Coursework ID'),
410
+ submissionId: z.string().describe('Submission ID'),
411
+ draft: z.string().optional().describe('Draft grade value'),
412
+ assigned: z.string().optional().describe('Assigned (final) grade value'),
413
+ account: accountParam,
414
+ },
415
+ }, async ({ courseId, courseworkId, submissionId, draft, assigned, account }) => {
416
+ const args = ['classroom', 'submissions', 'grade', courseId, courseworkId, submissionId];
417
+ if (draft) args.push(`--draft=${draft}`);
418
+ if (assigned) args.push(`--assigned=${assigned}`);
419
+ return runOrDiagnose(args, { account });
420
+ });
421
+
422
+ server.registerTool('gog_classroom_submissions_return', {
423
+ description: 'Return a graded submission to the student.',
424
+ annotations: { destructiveHint: true },
425
+ inputSchema: {
426
+ courseId: z.string().describe('Course ID'),
427
+ courseworkId: z.string().describe('Coursework ID'),
428
+ submissionId: z.string().describe('Submission ID'),
429
+ account: accountParam,
430
+ },
431
+ }, async ({ courseId, courseworkId, submissionId, account }) => {
432
+ return runOrDiagnose(['classroom', 'submissions', 'return', courseId, courseworkId, submissionId], { account });
433
+ });
434
+
435
+ server.registerTool('gog_classroom_submissions_turn_in', {
436
+ description: 'Turn in a student submission (student action).',
437
+ annotations: { destructiveHint: true },
438
+ inputSchema: {
439
+ courseId: z.string().describe('Course ID'),
440
+ courseworkId: z.string().describe('Coursework ID'),
441
+ submissionId: z.string().describe('Submission ID'),
442
+ account: accountParam,
443
+ },
444
+ }, async ({ courseId, courseworkId, submissionId, account }) => {
445
+ return runOrDiagnose(['classroom', 'submissions', 'turn-in', courseId, courseworkId, submissionId], { account });
446
+ });
447
+
448
+ server.registerTool('gog_classroom_submissions_reclaim', {
449
+ description: 'Reclaim a turned-in submission (student action to edit a submission).',
450
+ annotations: { destructiveHint: true },
451
+ inputSchema: {
452
+ courseId: z.string().describe('Course ID'),
453
+ courseworkId: z.string().describe('Coursework ID'),
454
+ submissionId: z.string().describe('Submission ID'),
455
+ account: accountParam,
456
+ },
457
+ }, async ({ courseId, courseworkId, submissionId, account }) => {
458
+ return runOrDiagnose(['classroom', 'submissions', 'reclaim', courseId, courseworkId, submissionId], { account });
459
+ });
460
+
461
+ server.registerTool('gog_classroom_announcements_list', {
462
+ description: 'List announcements in a Google Classroom course.',
463
+ annotations: { readOnlyHint: true },
464
+ inputSchema: {
465
+ courseId: z.string().describe('Course ID'),
466
+ state: z.string().optional().describe('Filter by announcement state'),
467
+ orderBy: z.string().optional().describe('Sort order'),
468
+ max: z.number().optional().describe('Max results per page'),
469
+ page: z.string().optional().describe('Page token'),
470
+ all: z.boolean().optional().describe('Fetch all pages'),
471
+ account: accountParam,
472
+ },
473
+ }, async ({ courseId, state, orderBy, max, page, all, account }) => {
474
+ const args = ['classroom', 'announcements', 'list', courseId];
475
+ if (state) args.push(`--state=${state}`);
476
+ if (orderBy) args.push(`--order-by=${orderBy}`);
477
+ if (max !== undefined) args.push(`--max=${max}`);
478
+ if (page) args.push(`--page=${page}`);
479
+ if (all) args.push('--all');
480
+ return runOrDiagnose(args, { account });
481
+ });
482
+
483
+ server.registerTool('gog_classroom_announcements_get', {
484
+ description: 'Get a single announcement by ID.',
485
+ annotations: { readOnlyHint: true },
486
+ inputSchema: {
487
+ courseId: z.string().describe('Course ID'),
488
+ announcementId: z.string().describe('Announcement ID'),
489
+ account: accountParam,
490
+ },
491
+ }, async ({ courseId, announcementId, account }) => {
492
+ return runOrDiagnose(['classroom', 'announcements', 'get', courseId, announcementId], { account });
493
+ });
494
+
495
+ server.registerTool('gog_classroom_announcements_create', {
496
+ description: 'Create an announcement in a Google Classroom course.',
497
+ inputSchema: {
498
+ courseId: z.string().describe('Course ID'),
499
+ text: z.string().describe('Announcement text'),
500
+ state: z.string().optional().describe('State: PUBLISHED or DRAFT'),
501
+ scheduled: z.string().optional().describe('Scheduled publish time'),
502
+ account: accountParam,
503
+ },
504
+ }, async ({ courseId, text, state, scheduled, account }) => {
505
+ const args = ['classroom', 'announcements', 'create', courseId, `--text=${text}`];
506
+ if (state) args.push(`--state=${state}`);
507
+ if (scheduled) args.push(`--scheduled=${scheduled}`);
508
+ return runOrDiagnose(args, { account });
509
+ });
510
+
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
+ server.registerTool('gog_classroom_topics_list', {
543
+ description: 'List topics in a Google Classroom course.',
544
+ annotations: { readOnlyHint: true },
545
+ inputSchema: {
546
+ courseId: z.string().describe('Course ID'),
547
+ max: z.number().optional().describe('Max results per page'),
548
+ page: z.string().optional().describe('Page token'),
549
+ all: z.boolean().optional().describe('Fetch all pages'),
550
+ account: accountParam,
551
+ },
552
+ }, async ({ courseId, max, page, all, account }) => {
553
+ const args = ['classroom', 'topics', 'list', courseId];
554
+ if (max !== undefined) args.push(`--max=${max}`);
555
+ if (page) args.push(`--page=${page}`);
556
+ if (all) args.push('--all');
557
+ return runOrDiagnose(args, { account });
558
+ });
559
+
560
+ server.registerTool('gog_classroom_topics_get', {
561
+ description: 'Get a single topic by ID.',
562
+ annotations: { readOnlyHint: true },
563
+ inputSchema: {
564
+ courseId: z.string().describe('Course ID'),
565
+ topicId: z.string().describe('Topic ID'),
566
+ account: accountParam,
567
+ },
568
+ }, async ({ courseId, topicId, account }) => {
569
+ return runOrDiagnose(['classroom', 'topics', 'get', courseId, topicId], { account });
570
+ });
571
+
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
+ server.registerTool('gog_classroom_invitations_list', {
609
+ description: 'List Google Classroom invitations.',
610
+ annotations: { readOnlyHint: true },
611
+ inputSchema: {
612
+ course: z.string().optional().describe('Filter by course ID'),
613
+ user: z.string().optional().describe('Filter by user ID'),
614
+ max: z.number().optional().describe('Max results per page'),
615
+ page: z.string().optional().describe('Page token'),
616
+ all: z.boolean().optional().describe('Fetch all pages'),
617
+ account: accountParam,
618
+ },
619
+ }, async ({ course, user, max, page, all, account }) => {
620
+ const args = ['classroom', 'invitations', 'list'];
621
+ if (course) args.push(`--course=${course}`);
622
+ if (user) args.push(`--user=${user}`);
623
+ if (max !== undefined) args.push(`--max=${max}`);
624
+ if (page) args.push(`--page=${page}`);
625
+ if (all) args.push('--all');
626
+ return runOrDiagnose(args, { account });
627
+ });
628
+
629
+ server.registerTool('gog_classroom_invitations_get', {
630
+ description: 'Get a single invitation by ID.',
631
+ annotations: { readOnlyHint: true },
632
+ inputSchema: {
633
+ invitationId: z.string().describe('Invitation ID'),
634
+ account: accountParam,
635
+ },
636
+ }, async ({ invitationId, account }) => {
637
+ return runOrDiagnose(['classroom', 'invitations', 'get', invitationId], { account });
638
+ });
639
+
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
+ server.registerTool('gog_classroom_invitations_accept', {
653
+ description: 'Accept a Google Classroom invitation.',
654
+ inputSchema: {
655
+ invitationId: z.string().describe('Invitation ID'),
656
+ account: accountParam,
657
+ },
658
+ }, async ({ invitationId, account }) => {
659
+ return runOrDiagnose(['classroom', 'invitations', 'accept', invitationId], { account });
660
+ });
661
+
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
+ server.registerTool('gog_classroom_profile_get', {
674
+ description: 'Get a Google Classroom user profile. Omit userId to fetch the authenticated user.',
675
+ annotations: { readOnlyHint: true },
676
+ inputSchema: {
677
+ userId: z.string().optional().describe('User ID (omit for self)'),
678
+ account: accountParam,
679
+ },
680
+ }, async ({ userId, account }) => {
681
+ const args = ['classroom', 'profile', 'get'];
682
+ if (userId) args.push(userId);
683
+ return runOrDiagnose(args, { account });
684
+ });
685
+
686
+ server.registerTool('gog_classroom_run', {
687
+ 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.',
688
+ inputSchema: {
689
+ subcommand: z.string().describe('The gog classroom subcommand to run, e.g. "guardians", "materials", "guardian-invitations"'),
690
+ args: z.array(z.string()).describe('Additional positional args and flags'),
691
+ account: accountParam,
692
+ },
693
+ }, async ({ subcommand, args, account }) => {
694
+ return runOrDiagnose(['classroom', subcommand, ...args], { account });
695
+ });
696
+ }