gogcli-mcp-classroom 2.0.4 → 2.0.6

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.
@@ -6,16 +6,16 @@
6
6
  "email": "chris.c.hall@gmail.com"
7
7
  },
8
8
  "metadata": {
9
- "description": "Extended Google Classroom for Claude via gogcli auth + full Classroom support (courses, rosters, coursework, submissions, announcements, topics, invitations)",
10
- "version": "2.0.4"
9
+ "description": "Extended Google Classroom for Claude via gogcli \u2014 auth + full Classroom support (courses, rosters, coursework, submissions, announcements, topics, invitations)",
10
+ "version": "2.0.3"
11
11
  },
12
12
  "plugins": [
13
13
  {
14
14
  "name": "gogcli-mcp-classroom",
15
15
  "displayName": "gogcli (Classroom)",
16
16
  "source": "./",
17
- "description": "Extended Google Classroom for Claude via gogcli auth + full Classroom support (courses, rosters, coursework, submissions, announcements, topics, invitations)",
18
- "version": "2.0.4",
17
+ "description": "Extended Google Classroom for Claude via gogcli \u2014 auth + full Classroom support (courses, rosters, coursework, submissions, announcements, topics, invitations)",
18
+ "version": "2.0.3",
19
19
  "author": {
20
20
  "name": "Chris Hall"
21
21
  },
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "gogcli-mcp-classroom",
3
3
  "displayName": "gogcli (Classroom)",
4
- "version": "2.0.4",
5
- "description": "Extended Google Classroom for Claude via gogcli auth + full Classroom support (courses, rosters, coursework, submissions, announcements, topics, invitations)",
4
+ "version": "2.0.3",
5
+ "description": "Extended Google Classroom for Claude via gogcli \u2014 auth + full Classroom support (courses, rosters, coursework, submissions, announcements, topics, invitations)",
6
6
  "author": {
7
7
  "name": "Chris Hall",
8
8
  "email": "chris.c.hall@gmail.com"
package/dist/index.js CHANGED
@@ -31061,83 +31061,6 @@ function registerClassroomTools(server2) {
31061
31061
  }, async ({ courseId, account }) => {
31062
31062
  return runOrDiagnose(["classroom", "courses", "get", courseId], { account });
31063
31063
  });
31064
- server2.registerTool("gog_classroom_courses_create", {
31065
- description: "Create a new Google Classroom course.",
31066
- inputSchema: {
31067
- name: external_exports.string().describe("Course name"),
31068
- owner: external_exports.string().optional().describe('Owner user ID (default: "me")'),
31069
- section: external_exports.string().optional().describe("Section"),
31070
- descriptionHeading: external_exports.string().optional().describe("Description heading"),
31071
- description: external_exports.string().optional().describe("Description"),
31072
- room: external_exports.string().optional().describe("Room"),
31073
- state: external_exports.string().optional().describe("Course state: ACTIVE, ARCHIVED, PROVISIONED, DECLINED, SUSPENDED"),
31074
- account: accountParam
31075
- }
31076
- }, async ({ name, owner, section, descriptionHeading, description, room, state, account }) => {
31077
- const args = ["classroom", "courses", "create", `--name=${name}`];
31078
- if (owner) args.push(`--owner=${owner}`);
31079
- if (section) args.push(`--section=${section}`);
31080
- if (descriptionHeading) args.push(`--description-heading=${descriptionHeading}`);
31081
- if (description) args.push(`--description=${description}`);
31082
- if (room) args.push(`--room=${room}`);
31083
- if (state) args.push(`--state=${state}`);
31084
- return runOrDiagnose(args, { account });
31085
- });
31086
- server2.registerTool("gog_classroom_courses_update", {
31087
- description: "Update an existing Google Classroom course.",
31088
- annotations: { destructiveHint: true },
31089
- inputSchema: {
31090
- courseId: external_exports.string().describe("Course ID"),
31091
- name: external_exports.string().optional().describe("Course name"),
31092
- owner: external_exports.string().optional().describe("Owner user ID"),
31093
- section: external_exports.string().optional().describe("Section"),
31094
- descriptionHeading: external_exports.string().optional().describe("Description heading"),
31095
- description: external_exports.string().optional().describe("Description"),
31096
- room: external_exports.string().optional().describe("Room"),
31097
- state: external_exports.string().optional().describe("Course state: ACTIVE, ARCHIVED, PROVISIONED, DECLINED, SUSPENDED"),
31098
- account: accountParam
31099
- }
31100
- }, async ({ courseId, name, owner, section, descriptionHeading, description, room, state, account }) => {
31101
- const args = ["classroom", "courses", "update", courseId];
31102
- if (name) args.push(`--name=${name}`);
31103
- if (owner) args.push(`--owner=${owner}`);
31104
- if (section) args.push(`--section=${section}`);
31105
- if (descriptionHeading) args.push(`--description-heading=${descriptionHeading}`);
31106
- if (description) args.push(`--description=${description}`);
31107
- if (room) args.push(`--room=${room}`);
31108
- if (state) args.push(`--state=${state}`);
31109
- return runOrDiagnose(args, { account });
31110
- });
31111
- server2.registerTool("gog_classroom_courses_delete", {
31112
- description: "Delete a Google Classroom course.",
31113
- annotations: { destructiveHint: true },
31114
- inputSchema: {
31115
- courseId: external_exports.string().describe("Course ID"),
31116
- account: accountParam
31117
- }
31118
- }, async ({ courseId, account }) => {
31119
- return runOrDiagnose(["classroom", "courses", "delete", courseId], { account });
31120
- });
31121
- server2.registerTool("gog_classroom_courses_archive", {
31122
- description: "Archive a Google Classroom course.",
31123
- annotations: { destructiveHint: true },
31124
- inputSchema: {
31125
- courseId: external_exports.string().describe("Course ID"),
31126
- account: accountParam
31127
- }
31128
- }, async ({ courseId, account }) => {
31129
- return runOrDiagnose(["classroom", "courses", "archive", courseId], { account });
31130
- });
31131
- server2.registerTool("gog_classroom_courses_unarchive", {
31132
- description: "Unarchive a Google Classroom course (restore to ACTIVE).",
31133
- annotations: { destructiveHint: true },
31134
- inputSchema: {
31135
- courseId: external_exports.string().describe("Course ID"),
31136
- account: accountParam
31137
- }
31138
- }, async ({ courseId, account }) => {
31139
- return runOrDiagnose(["classroom", "courses", "unarchive", courseId], { account });
31140
- });
31141
31064
  server2.registerTool("gog_classroom_students_list", {
31142
31065
  description: "List students enrolled in a Google Classroom course.",
31143
31066
  annotations: { readOnlyHint: true },
@@ -31166,30 +31089,6 @@ function registerClassroomTools(server2) {
31166
31089
  }, async ({ courseId, userId, account }) => {
31167
31090
  return runOrDiagnose(["classroom", "students", "get", courseId, userId], { account });
31168
31091
  });
31169
- server2.registerTool("gog_classroom_students_add", {
31170
- description: "Add a student to a Google Classroom course.",
31171
- inputSchema: {
31172
- courseId: external_exports.string().describe("Course ID"),
31173
- userId: external_exports.string().describe('Student user ID (or "me")'),
31174
- enrollmentCode: external_exports.string().optional().describe("Enrollment code (required if adding self via code)"),
31175
- account: accountParam
31176
- }
31177
- }, async ({ courseId, userId, enrollmentCode, account }) => {
31178
- const args = ["classroom", "students", "add", courseId, userId];
31179
- if (enrollmentCode) args.push(`--enrollment-code=${enrollmentCode}`);
31180
- return runOrDiagnose(args, { account });
31181
- });
31182
- server2.registerTool("gog_classroom_students_remove", {
31183
- description: "Remove a student from a Google Classroom course.",
31184
- annotations: { destructiveHint: true },
31185
- inputSchema: {
31186
- courseId: external_exports.string().describe("Course ID"),
31187
- userId: external_exports.string().describe("Student user ID"),
31188
- account: accountParam
31189
- }
31190
- }, async ({ courseId, userId, account }) => {
31191
- return runOrDiagnose(["classroom", "students", "remove", courseId, userId], { account });
31192
- });
31193
31092
  server2.registerTool("gog_classroom_teachers_list", {
31194
31093
  description: "List teachers in a Google Classroom course.",
31195
31094
  annotations: { readOnlyHint: true },
@@ -31218,27 +31117,6 @@ function registerClassroomTools(server2) {
31218
31117
  }, async ({ courseId, userId, account }) => {
31219
31118
  return runOrDiagnose(["classroom", "teachers", "get", courseId, userId], { account });
31220
31119
  });
31221
- server2.registerTool("gog_classroom_teachers_add", {
31222
- description: "Add a teacher to a Google Classroom course.",
31223
- inputSchema: {
31224
- courseId: external_exports.string().describe("Course ID"),
31225
- userId: external_exports.string().describe("Teacher user ID"),
31226
- account: accountParam
31227
- }
31228
- }, async ({ courseId, userId, account }) => {
31229
- return runOrDiagnose(["classroom", "teachers", "add", courseId, userId], { account });
31230
- });
31231
- server2.registerTool("gog_classroom_teachers_remove", {
31232
- description: "Remove a teacher from a Google Classroom course.",
31233
- annotations: { destructiveHint: true },
31234
- inputSchema: {
31235
- courseId: external_exports.string().describe("Course ID"),
31236
- userId: external_exports.string().describe("Teacher user ID"),
31237
- account: accountParam
31238
- }
31239
- }, async ({ courseId, userId, account }) => {
31240
- return runOrDiagnose(["classroom", "teachers", "remove", courseId, userId], { account });
31241
- });
31242
31120
  server2.registerTool("gog_classroom_roster", {
31243
31121
  description: "List the full roster (students and/or teachers) of a Google Classroom course. Omit both flags to return both groups.",
31244
31122
  annotations: { readOnlyHint: true },
@@ -31296,78 +31174,6 @@ function registerClassroomTools(server2) {
31296
31174
  }, async ({ courseId, courseworkId, account }) => {
31297
31175
  return runOrDiagnose(["classroom", "coursework", "get", courseId, courseworkId], { account });
31298
31176
  });
31299
- server2.registerTool("gog_classroom_coursework_create", {
31300
- description: "Create a new coursework item (assignment, question, etc.) in a course.",
31301
- inputSchema: {
31302
- courseId: external_exports.string().describe("Course ID"),
31303
- title: external_exports.string().describe("Coursework title"),
31304
- description: external_exports.string().optional().describe("Description"),
31305
- type: external_exports.string().optional().describe("Work type (ASSIGNMENT, SHORT_ANSWER_QUESTION, MULTIPLE_CHOICE_QUESTION). Default: ASSIGNMENT"),
31306
- state: external_exports.string().optional().describe("State: PUBLISHED or DRAFT"),
31307
- maxPoints: external_exports.number().optional().describe("Max points"),
31308
- due: external_exports.string().optional().describe("Due datetime (combined date+time)"),
31309
- dueDate: external_exports.string().optional().describe("Due date (YYYY-MM-DD)"),
31310
- dueTime: external_exports.string().optional().describe("Due time (HH:MM)"),
31311
- scheduled: external_exports.string().optional().describe("Scheduled publish time"),
31312
- topic: external_exports.string().optional().describe("Topic ID"),
31313
- account: accountParam
31314
- }
31315
- }, async ({ courseId, title, description, type, state, maxPoints, due, dueDate, dueTime, scheduled, topic, account }) => {
31316
- const args = ["classroom", "coursework", "create", courseId, `--title=${title}`];
31317
- if (description) args.push(`--description=${description}`);
31318
- if (type) args.push(`--type=${type}`);
31319
- if (state) args.push(`--state=${state}`);
31320
- if (maxPoints !== void 0) args.push(`--max-points=${maxPoints}`);
31321
- if (due) args.push(`--due=${due}`);
31322
- if (dueDate) args.push(`--due-date=${dueDate}`);
31323
- if (dueTime) args.push(`--due-time=${dueTime}`);
31324
- if (scheduled) args.push(`--scheduled=${scheduled}`);
31325
- if (topic) args.push(`--topic=${topic}`);
31326
- return runOrDiagnose(args, { account });
31327
- });
31328
- server2.registerTool("gog_classroom_coursework_update", {
31329
- description: "Update an existing coursework item.",
31330
- annotations: { destructiveHint: true },
31331
- inputSchema: {
31332
- courseId: external_exports.string().describe("Course ID"),
31333
- courseworkId: external_exports.string().describe("Coursework ID"),
31334
- title: external_exports.string().optional().describe("New title"),
31335
- description: external_exports.string().optional().describe("New description"),
31336
- type: external_exports.string().optional().describe("Work type"),
31337
- state: external_exports.string().optional().describe("State: PUBLISHED or DRAFT"),
31338
- maxPoints: external_exports.number().optional().describe("Max points"),
31339
- due: external_exports.string().optional().describe("Due datetime"),
31340
- dueDate: external_exports.string().optional().describe("Due date (YYYY-MM-DD)"),
31341
- dueTime: external_exports.string().optional().describe("Due time (HH:MM)"),
31342
- scheduled: external_exports.string().optional().describe("Scheduled publish time"),
31343
- topic: external_exports.string().optional().describe("Topic ID"),
31344
- account: accountParam
31345
- }
31346
- }, async ({ courseId, courseworkId, title, description, type, state, maxPoints, due, dueDate, dueTime, scheduled, topic, account }) => {
31347
- const args = ["classroom", "coursework", "update", courseId, courseworkId];
31348
- if (title) args.push(`--title=${title}`);
31349
- if (description) args.push(`--description=${description}`);
31350
- if (type) args.push(`--type=${type}`);
31351
- if (state) args.push(`--state=${state}`);
31352
- if (maxPoints !== void 0) args.push(`--max-points=${maxPoints}`);
31353
- if (due) args.push(`--due=${due}`);
31354
- if (dueDate) args.push(`--due-date=${dueDate}`);
31355
- if (dueTime) args.push(`--due-time=${dueTime}`);
31356
- if (scheduled) args.push(`--scheduled=${scheduled}`);
31357
- if (topic) args.push(`--topic=${topic}`);
31358
- return runOrDiagnose(args, { account });
31359
- });
31360
- server2.registerTool("gog_classroom_coursework_delete", {
31361
- description: "Delete a coursework item.",
31362
- annotations: { destructiveHint: true },
31363
- inputSchema: {
31364
- courseId: external_exports.string().describe("Course ID"),
31365
- courseworkId: external_exports.string().describe("Coursework ID"),
31366
- account: accountParam
31367
- }
31368
- }, async ({ courseId, courseworkId, account }) => {
31369
- return runOrDiagnose(["classroom", "coursework", "delete", courseId, courseworkId], { account });
31370
- });
31371
31177
  server2.registerTool("gog_classroom_submissions_list", {
31372
31178
  description: "List student submissions for a coursework item.",
31373
31179
  annotations: { readOnlyHint: true },
@@ -31494,7 +31300,7 @@ function registerClassroomTools(server2) {
31494
31300
  inputSchema: {
31495
31301
  courseId: external_exports.string().describe("Course ID"),
31496
31302
  text: external_exports.string().describe("Announcement text"),
31497
- state: external_exports.string().optional().describe("State: PUBLISHED or DRAFT"),
31303
+ state: external_exports.enum(["PUBLISHED", "DRAFT"]).optional().describe("State"),
31498
31304
  scheduled: external_exports.string().optional().describe("Scheduled publish time"),
31499
31305
  account: accountParam
31500
31306
  }
@@ -31504,35 +31310,6 @@ function registerClassroomTools(server2) {
31504
31310
  if (scheduled) args.push(`--scheduled=${scheduled}`);
31505
31311
  return runOrDiagnose(args, { account });
31506
31312
  });
31507
- server2.registerTool("gog_classroom_announcements_update", {
31508
- description: "Update an existing announcement.",
31509
- annotations: { destructiveHint: true },
31510
- inputSchema: {
31511
- courseId: external_exports.string().describe("Course ID"),
31512
- announcementId: external_exports.string().describe("Announcement ID"),
31513
- text: external_exports.string().optional().describe("New text"),
31514
- state: external_exports.string().optional().describe("State: PUBLISHED or DRAFT"),
31515
- scheduled: external_exports.string().optional().describe("Scheduled publish time"),
31516
- account: accountParam
31517
- }
31518
- }, async ({ courseId, announcementId, text, state, scheduled, account }) => {
31519
- const args = ["classroom", "announcements", "update", courseId, announcementId];
31520
- if (text) args.push(`--text=${text}`);
31521
- if (state) args.push(`--state=${state}`);
31522
- if (scheduled) args.push(`--scheduled=${scheduled}`);
31523
- return runOrDiagnose(args, { account });
31524
- });
31525
- server2.registerTool("gog_classroom_announcements_delete", {
31526
- description: "Delete an announcement.",
31527
- annotations: { destructiveHint: true },
31528
- inputSchema: {
31529
- courseId: external_exports.string().describe("Course ID"),
31530
- announcementId: external_exports.string().describe("Announcement ID"),
31531
- account: accountParam
31532
- }
31533
- }, async ({ courseId, announcementId, account }) => {
31534
- return runOrDiagnose(["classroom", "announcements", "delete", courseId, announcementId], { account });
31535
- });
31536
31313
  server2.registerTool("gog_classroom_topics_list", {
31537
31314
  description: "List topics in a Google Classroom course.",
31538
31315
  annotations: { readOnlyHint: true },
@@ -31561,39 +31338,6 @@ function registerClassroomTools(server2) {
31561
31338
  }, async ({ courseId, topicId, account }) => {
31562
31339
  return runOrDiagnose(["classroom", "topics", "get", courseId, topicId], { account });
31563
31340
  });
31564
- server2.registerTool("gog_classroom_topics_create", {
31565
- description: "Create a topic in a Google Classroom course.",
31566
- inputSchema: {
31567
- courseId: external_exports.string().describe("Course ID"),
31568
- name: external_exports.string().describe("Topic name"),
31569
- account: accountParam
31570
- }
31571
- }, async ({ courseId, name, account }) => {
31572
- return runOrDiagnose(["classroom", "topics", "create", courseId, `--name=${name}`], { account });
31573
- });
31574
- server2.registerTool("gog_classroom_topics_update", {
31575
- description: "Rename an existing topic.",
31576
- annotations: { destructiveHint: true },
31577
- inputSchema: {
31578
- courseId: external_exports.string().describe("Course ID"),
31579
- topicId: external_exports.string().describe("Topic ID"),
31580
- name: external_exports.string().describe("New topic name"),
31581
- account: accountParam
31582
- }
31583
- }, async ({ courseId, topicId, name, account }) => {
31584
- return runOrDiagnose(["classroom", "topics", "update", courseId, topicId, `--name=${name}`], { account });
31585
- });
31586
- server2.registerTool("gog_classroom_topics_delete", {
31587
- description: "Delete a topic.",
31588
- annotations: { destructiveHint: true },
31589
- inputSchema: {
31590
- courseId: external_exports.string().describe("Course ID"),
31591
- topicId: external_exports.string().describe("Topic ID"),
31592
- account: accountParam
31593
- }
31594
- }, async ({ courseId, topicId, account }) => {
31595
- return runOrDiagnose(["classroom", "topics", "delete", courseId, topicId], { account });
31596
- });
31597
31341
  server2.registerTool("gog_classroom_invitations_list", {
31598
31342
  description: "List Google Classroom invitations.",
31599
31343
  annotations: { readOnlyHint: true },
@@ -31624,17 +31368,6 @@ function registerClassroomTools(server2) {
31624
31368
  }, async ({ invitationId, account }) => {
31625
31369
  return runOrDiagnose(["classroom", "invitations", "get", invitationId], { account });
31626
31370
  });
31627
- server2.registerTool("gog_classroom_invitations_create", {
31628
- description: "Create an invitation to a Google Classroom course.",
31629
- inputSchema: {
31630
- courseId: external_exports.string().describe("Course ID"),
31631
- userId: external_exports.string().describe("User ID to invite"),
31632
- role: external_exports.enum(["STUDENT", "TEACHER", "OWNER"]).describe("Role for the invited user"),
31633
- account: accountParam
31634
- }
31635
- }, async ({ courseId, userId, role, account }) => {
31636
- return runOrDiagnose(["classroom", "invitations", "create", courseId, userId, `--role=${role}`], { account });
31637
- });
31638
31371
  server2.registerTool("gog_classroom_invitations_accept", {
31639
31372
  description: "Accept a Google Classroom invitation.",
31640
31373
  inputSchema: {
@@ -31644,16 +31377,6 @@ function registerClassroomTools(server2) {
31644
31377
  }, async ({ invitationId, account }) => {
31645
31378
  return runOrDiagnose(["classroom", "invitations", "accept", invitationId], { account });
31646
31379
  });
31647
- server2.registerTool("gog_classroom_invitations_delete", {
31648
- description: "Delete (revoke) a Google Classroom invitation.",
31649
- annotations: { destructiveHint: true },
31650
- inputSchema: {
31651
- invitationId: external_exports.string().describe("Invitation ID"),
31652
- account: accountParam
31653
- }
31654
- }, async ({ invitationId, account }) => {
31655
- return runOrDiagnose(["classroom", "invitations", "delete", invitationId], { account });
31656
- });
31657
31380
  server2.registerTool("gog_classroom_profile_get", {
31658
31381
  description: "Get a Google Classroom user profile. Omit userId to fetch the authenticated user.",
31659
31382
  annotations: { readOnlyHint: true },
@@ -31668,6 +31391,7 @@ function registerClassroomTools(server2) {
31668
31391
  });
31669
31392
  server2.registerTool("gog_classroom_run", {
31670
31393
  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.",
31394
+ annotations: { destructiveHint: true },
31671
31395
  inputSchema: {
31672
31396
  subcommand: external_exports.string().describe('The gog classroom subcommand to run, e.g. "guardians", "materials", "guardian-invitations"'),
31673
31397
  args: external_exports.array(external_exports.string()).describe("Additional positional args and flags"),
@@ -31679,7 +31403,7 @@ function registerClassroomTools(server2) {
31679
31403
  }
31680
31404
 
31681
31405
  // ../gogcli-mcp/src/server.ts
31682
- var VERSION = true ? "2.0.4" : "0.0.0";
31406
+ var VERSION = true ? "2.0.5" : "0.0.0";
31683
31407
  function createServer(options) {
31684
31408
  return new McpServer({
31685
31409
  name: options?.name ?? "gogcli",
@@ -31688,7 +31412,279 @@ function createServer(options) {
31688
31412
  }
31689
31413
 
31690
31414
  // src/tools/classroom-extra.ts
31691
- function registerExtraClassroomTools(_server) {
31415
+ var courseState = external_exports.enum(["ACTIVE", "ARCHIVED", "PROVISIONED", "DECLINED", "SUSPENDED"]);
31416
+ var workState = external_exports.enum(["PUBLISHED", "DRAFT"]);
31417
+ var workType = external_exports.enum(["ASSIGNMENT", "SHORT_ANSWER_QUESTION", "MULTIPLE_CHOICE_QUESTION"]);
31418
+ var courseSharedFields = {
31419
+ owner: external_exports.string().optional().describe('Owner user ID (default "me" on create)'),
31420
+ section: external_exports.string().optional().describe("Section"),
31421
+ descriptionHeading: external_exports.string().optional().describe("Description heading"),
31422
+ description: external_exports.string().optional().describe("Description"),
31423
+ room: external_exports.string().optional().describe("Room"),
31424
+ state: courseState.optional().describe("Course state")
31425
+ };
31426
+ var courseworkSharedFields = {
31427
+ description: external_exports.string().optional().describe("Description"),
31428
+ type: workType.optional().describe("Work type (default: ASSIGNMENT)"),
31429
+ state: workState.optional().describe("State"),
31430
+ maxPoints: external_exports.number().optional().describe("Max points"),
31431
+ due: external_exports.string().optional().describe("Due datetime (combined date+time)"),
31432
+ dueDate: external_exports.string().optional().describe("Due date (YYYY-MM-DD)"),
31433
+ dueTime: external_exports.string().optional().describe("Due time (HH:MM)"),
31434
+ scheduled: external_exports.string().optional().describe("Scheduled publish time"),
31435
+ topic: external_exports.string().optional().describe("Topic ID")
31436
+ };
31437
+ function registerExtraClassroomTools(server2) {
31438
+ server2.registerTool("gog_classroom_courses_create", {
31439
+ description: "Create a new Google Classroom course.",
31440
+ inputSchema: {
31441
+ name: external_exports.string().describe("Course name"),
31442
+ ...courseSharedFields,
31443
+ account: accountParam
31444
+ }
31445
+ }, async ({ name, owner, section, descriptionHeading, description, room, state, account }) => {
31446
+ const args = ["classroom", "courses", "create", `--name=${name}`];
31447
+ if (owner) args.push(`--owner=${owner}`);
31448
+ if (section) args.push(`--section=${section}`);
31449
+ if (descriptionHeading) args.push(`--description-heading=${descriptionHeading}`);
31450
+ if (description) args.push(`--description=${description}`);
31451
+ if (room) args.push(`--room=${room}`);
31452
+ if (state) args.push(`--state=${state}`);
31453
+ return runOrDiagnose(args, { account });
31454
+ });
31455
+ server2.registerTool("gog_classroom_courses_update", {
31456
+ description: "Update an existing Google Classroom course.",
31457
+ annotations: { destructiveHint: true },
31458
+ inputSchema: {
31459
+ courseId: external_exports.string().describe("Course ID"),
31460
+ name: external_exports.string().optional().describe("Course name"),
31461
+ ...courseSharedFields,
31462
+ account: accountParam
31463
+ }
31464
+ }, async ({ courseId, name, owner, section, descriptionHeading, description, room, state, account }) => {
31465
+ const args = ["classroom", "courses", "update", courseId];
31466
+ if (name) args.push(`--name=${name}`);
31467
+ if (owner) args.push(`--owner=${owner}`);
31468
+ if (section) args.push(`--section=${section}`);
31469
+ if (descriptionHeading) args.push(`--description-heading=${descriptionHeading}`);
31470
+ if (description) args.push(`--description=${description}`);
31471
+ if (room) args.push(`--room=${room}`);
31472
+ if (state) args.push(`--state=${state}`);
31473
+ return runOrDiagnose(args, { account });
31474
+ });
31475
+ server2.registerTool("gog_classroom_courses_delete", {
31476
+ description: "Delete a Google Classroom course.",
31477
+ annotations: { destructiveHint: true },
31478
+ inputSchema: {
31479
+ courseId: external_exports.string().describe("Course ID"),
31480
+ account: accountParam
31481
+ }
31482
+ }, async ({ courseId, account }) => {
31483
+ return runOrDiagnose(["classroom", "courses", "delete", courseId], { account });
31484
+ });
31485
+ server2.registerTool("gog_classroom_courses_archive", {
31486
+ description: "Archive a Google Classroom course.",
31487
+ annotations: { destructiveHint: true },
31488
+ inputSchema: {
31489
+ courseId: external_exports.string().describe("Course ID"),
31490
+ account: accountParam
31491
+ }
31492
+ }, async ({ courseId, account }) => {
31493
+ return runOrDiagnose(["classroom", "courses", "archive", courseId], { account });
31494
+ });
31495
+ server2.registerTool("gog_classroom_courses_unarchive", {
31496
+ description: "Unarchive a Google Classroom course (restore to ACTIVE).",
31497
+ inputSchema: {
31498
+ courseId: external_exports.string().describe("Course ID"),
31499
+ account: accountParam
31500
+ }
31501
+ }, async ({ courseId, account }) => {
31502
+ return runOrDiagnose(["classroom", "courses", "unarchive", courseId], { account });
31503
+ });
31504
+ server2.registerTool("gog_classroom_students_add", {
31505
+ description: "Add a student to a Google Classroom course.",
31506
+ inputSchema: {
31507
+ courseId: external_exports.string().describe("Course ID"),
31508
+ userId: external_exports.string().describe('Student user ID (or "me")'),
31509
+ enrollmentCode: external_exports.string().optional().describe("Enrollment code (required if adding self via code)"),
31510
+ account: accountParam
31511
+ }
31512
+ }, async ({ courseId, userId, enrollmentCode, account }) => {
31513
+ const args = ["classroom", "students", "add", courseId, userId];
31514
+ if (enrollmentCode) args.push(`--enrollment-code=${enrollmentCode}`);
31515
+ return runOrDiagnose(args, { account });
31516
+ });
31517
+ server2.registerTool("gog_classroom_students_remove", {
31518
+ description: "Remove a student from a Google Classroom course.",
31519
+ annotations: { destructiveHint: true },
31520
+ inputSchema: {
31521
+ courseId: external_exports.string().describe("Course ID"),
31522
+ userId: external_exports.string().describe("Student user ID"),
31523
+ account: accountParam
31524
+ }
31525
+ }, async ({ courseId, userId, account }) => {
31526
+ return runOrDiagnose(["classroom", "students", "remove", courseId, userId], { account });
31527
+ });
31528
+ server2.registerTool("gog_classroom_teachers_add", {
31529
+ description: "Add a teacher to a Google Classroom course.",
31530
+ inputSchema: {
31531
+ courseId: external_exports.string().describe("Course ID"),
31532
+ userId: external_exports.string().describe("Teacher user ID"),
31533
+ account: accountParam
31534
+ }
31535
+ }, async ({ courseId, userId, account }) => {
31536
+ return runOrDiagnose(["classroom", "teachers", "add", courseId, userId], { account });
31537
+ });
31538
+ server2.registerTool("gog_classroom_teachers_remove", {
31539
+ description: "Remove a teacher from a Google Classroom course.",
31540
+ annotations: { destructiveHint: true },
31541
+ inputSchema: {
31542
+ courseId: external_exports.string().describe("Course ID"),
31543
+ userId: external_exports.string().describe("Teacher user ID"),
31544
+ account: accountParam
31545
+ }
31546
+ }, async ({ courseId, userId, account }) => {
31547
+ return runOrDiagnose(["classroom", "teachers", "remove", courseId, userId], { account });
31548
+ });
31549
+ server2.registerTool("gog_classroom_coursework_create", {
31550
+ description: "Create a new coursework item (assignment, question, etc.) in a course.",
31551
+ inputSchema: {
31552
+ courseId: external_exports.string().describe("Course ID"),
31553
+ title: external_exports.string().describe("Coursework title"),
31554
+ ...courseworkSharedFields,
31555
+ account: accountParam
31556
+ }
31557
+ }, async ({ courseId, title, description, type, state, maxPoints, due, dueDate, dueTime, scheduled, topic, account }) => {
31558
+ const args = ["classroom", "coursework", "create", courseId, `--title=${title}`];
31559
+ if (description) args.push(`--description=${description}`);
31560
+ if (type) args.push(`--type=${type}`);
31561
+ if (state) args.push(`--state=${state}`);
31562
+ if (maxPoints !== void 0) args.push(`--max-points=${maxPoints}`);
31563
+ if (due) args.push(`--due=${due}`);
31564
+ if (dueDate) args.push(`--due-date=${dueDate}`);
31565
+ if (dueTime) args.push(`--due-time=${dueTime}`);
31566
+ if (scheduled) args.push(`--scheduled=${scheduled}`);
31567
+ if (topic) args.push(`--topic=${topic}`);
31568
+ return runOrDiagnose(args, { account });
31569
+ });
31570
+ server2.registerTool("gog_classroom_coursework_update", {
31571
+ description: "Update an existing coursework item.",
31572
+ annotations: { destructiveHint: true },
31573
+ inputSchema: {
31574
+ courseId: external_exports.string().describe("Course ID"),
31575
+ courseworkId: external_exports.string().describe("Coursework ID"),
31576
+ title: external_exports.string().optional().describe("New title"),
31577
+ ...courseworkSharedFields,
31578
+ account: accountParam
31579
+ }
31580
+ }, async ({ courseId, courseworkId, title, description, type, state, maxPoints, due, dueDate, dueTime, scheduled, topic, account }) => {
31581
+ const args = ["classroom", "coursework", "update", courseId, courseworkId];
31582
+ if (title) args.push(`--title=${title}`);
31583
+ if (description) args.push(`--description=${description}`);
31584
+ if (type) args.push(`--type=${type}`);
31585
+ if (state) args.push(`--state=${state}`);
31586
+ if (maxPoints !== void 0) args.push(`--max-points=${maxPoints}`);
31587
+ if (due) args.push(`--due=${due}`);
31588
+ if (dueDate) args.push(`--due-date=${dueDate}`);
31589
+ if (dueTime) args.push(`--due-time=${dueTime}`);
31590
+ if (scheduled) args.push(`--scheduled=${scheduled}`);
31591
+ if (topic) args.push(`--topic=${topic}`);
31592
+ return runOrDiagnose(args, { account });
31593
+ });
31594
+ server2.registerTool("gog_classroom_coursework_delete", {
31595
+ description: "Delete a coursework item.",
31596
+ annotations: { destructiveHint: true },
31597
+ inputSchema: {
31598
+ courseId: external_exports.string().describe("Course ID"),
31599
+ courseworkId: external_exports.string().describe("Coursework ID"),
31600
+ account: accountParam
31601
+ }
31602
+ }, async ({ courseId, courseworkId, account }) => {
31603
+ return runOrDiagnose(["classroom", "coursework", "delete", courseId, courseworkId], { account });
31604
+ });
31605
+ server2.registerTool("gog_classroom_announcements_update", {
31606
+ description: "Update an existing announcement.",
31607
+ annotations: { destructiveHint: true },
31608
+ inputSchema: {
31609
+ courseId: external_exports.string().describe("Course ID"),
31610
+ announcementId: external_exports.string().describe("Announcement ID"),
31611
+ text: external_exports.string().optional().describe("New text"),
31612
+ state: workState.optional().describe("State"),
31613
+ scheduled: external_exports.string().optional().describe("Scheduled publish time"),
31614
+ account: accountParam
31615
+ }
31616
+ }, async ({ courseId, announcementId, text, state, scheduled, account }) => {
31617
+ const args = ["classroom", "announcements", "update", courseId, announcementId];
31618
+ if (text) args.push(`--text=${text}`);
31619
+ if (state) args.push(`--state=${state}`);
31620
+ if (scheduled) args.push(`--scheduled=${scheduled}`);
31621
+ return runOrDiagnose(args, { account });
31622
+ });
31623
+ server2.registerTool("gog_classroom_announcements_delete", {
31624
+ description: "Delete an announcement.",
31625
+ annotations: { destructiveHint: true },
31626
+ inputSchema: {
31627
+ courseId: external_exports.string().describe("Course ID"),
31628
+ announcementId: external_exports.string().describe("Announcement ID"),
31629
+ account: accountParam
31630
+ }
31631
+ }, async ({ courseId, announcementId, account }) => {
31632
+ return runOrDiagnose(["classroom", "announcements", "delete", courseId, announcementId], { account });
31633
+ });
31634
+ server2.registerTool("gog_classroom_topics_create", {
31635
+ description: "Create a topic in a Google Classroom course.",
31636
+ inputSchema: {
31637
+ courseId: external_exports.string().describe("Course ID"),
31638
+ name: external_exports.string().describe("Topic name"),
31639
+ account: accountParam
31640
+ }
31641
+ }, async ({ courseId, name, account }) => {
31642
+ return runOrDiagnose(["classroom", "topics", "create", courseId, `--name=${name}`], { account });
31643
+ });
31644
+ server2.registerTool("gog_classroom_topics_update", {
31645
+ description: "Rename an existing topic.",
31646
+ annotations: { destructiveHint: true },
31647
+ inputSchema: {
31648
+ courseId: external_exports.string().describe("Course ID"),
31649
+ topicId: external_exports.string().describe("Topic ID"),
31650
+ name: external_exports.string().describe("New topic name"),
31651
+ account: accountParam
31652
+ }
31653
+ }, async ({ courseId, topicId, name, account }) => {
31654
+ return runOrDiagnose(["classroom", "topics", "update", courseId, topicId, `--name=${name}`], { account });
31655
+ });
31656
+ server2.registerTool("gog_classroom_topics_delete", {
31657
+ description: "Delete a topic.",
31658
+ annotations: { destructiveHint: true },
31659
+ inputSchema: {
31660
+ courseId: external_exports.string().describe("Course ID"),
31661
+ topicId: external_exports.string().describe("Topic ID"),
31662
+ account: accountParam
31663
+ }
31664
+ }, async ({ courseId, topicId, account }) => {
31665
+ return runOrDiagnose(["classroom", "topics", "delete", courseId, topicId], { account });
31666
+ });
31667
+ server2.registerTool("gog_classroom_invitations_create", {
31668
+ description: "Create an invitation to a Google Classroom course.",
31669
+ inputSchema: {
31670
+ courseId: external_exports.string().describe("Course ID"),
31671
+ userId: external_exports.string().describe("User ID to invite"),
31672
+ role: external_exports.enum(["STUDENT", "TEACHER", "OWNER"]).describe("Role for the invited user"),
31673
+ account: accountParam
31674
+ }
31675
+ }, async ({ courseId, userId, role, account }) => {
31676
+ return runOrDiagnose(["classroom", "invitations", "create", courseId, userId, `--role=${role}`], { account });
31677
+ });
31678
+ server2.registerTool("gog_classroom_invitations_delete", {
31679
+ description: "Delete (revoke) a Google Classroom invitation.",
31680
+ annotations: { destructiveHint: true },
31681
+ inputSchema: {
31682
+ invitationId: external_exports.string().describe("Invitation ID"),
31683
+ account: accountParam
31684
+ }
31685
+ }, async ({ invitationId, account }) => {
31686
+ return runOrDiagnose(["classroom", "invitations", "delete", invitationId], { account });
31687
+ });
31692
31688
  }
31693
31689
 
31694
31690
  // src/index.ts
package/manifest.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "manifest_version": "0.3",
4
4
  "name": "gogcli-mcp-classroom",
5
5
  "display_name": "gogcli (Classroom)",
6
- "version": "2.0.4",
6
+ "version": "2.0.6",
7
7
  "description": "Extended Google Classroom for Claude via gogcli — auth + full Classroom support (courses, rosters, coursework, submissions, announcements, topics, invitations)",
8
8
  "author": {
9
9
  "name": "Chris Hall",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gogcli-mcp-classroom",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "mcpName": "io.github.chrischall/gogcli-mcp-classroom",
5
5
  "description": "Extended Google Classroom MCP server via gogcli — auth + full Classroom support",
6
6
  "author": "Claude Code (AI) <https://www.anthropic.com/claude>",
package/server.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.chrischall/gogcli-mcp-classroom",
4
- "description": "Google Classroom via gogcli for Claude courses, assignments, submissions, grading",
4
+ "description": "Google Classroom via gogcli for Claude \u2014 courses, assignments, submissions, grading",
5
5
  "repository": {
6
6
  "url": "https://github.com/chrischall/gogcli-mcp",
7
7
  "source": "github",
8
8
  "subfolder": "packages/gogcli-mcp-classroom"
9
9
  },
10
- "version": "2.0.4",
10
+ "version": "2.0.3",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "gogcli-mcp-classroom",
15
- "version": "2.0.4",
15
+ "version": "2.0.3",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  },
@@ -1,5 +1,303 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { z } from 'zod';
3
+ import { accountParam, runOrDiagnose } from '../../../gogcli-mcp/src/lib.js';
2
4
 
3
- export function registerExtraClassroomTools(_server: McpServer): void {
4
- /* no extras yet */
5
+ const courseState = z.enum(['ACTIVE', 'ARCHIVED', 'PROVISIONED', 'DECLINED', 'SUSPENDED']);
6
+ const workState = z.enum(['PUBLISHED', 'DRAFT']);
7
+ const workType = z.enum(['ASSIGNMENT', 'SHORT_ANSWER_QUESTION', 'MULTIPLE_CHOICE_QUESTION']);
8
+
9
+ // Fields shared by courses_create and courses_update. `name` is required on
10
+ // create, optional on update — keep it out of this fragment so each tool can
11
+ // declare its own rule.
12
+ const courseSharedFields = {
13
+ owner: z.string().optional().describe('Owner user ID (default "me" on create)'),
14
+ section: z.string().optional().describe('Section'),
15
+ descriptionHeading: z.string().optional().describe('Description heading'),
16
+ description: z.string().optional().describe('Description'),
17
+ room: z.string().optional().describe('Room'),
18
+ state: courseState.optional().describe('Course state'),
19
+ };
20
+
21
+ // Fields shared by coursework_create and coursework_update.
22
+ const courseworkSharedFields = {
23
+ description: z.string().optional().describe('Description'),
24
+ type: workType.optional().describe('Work type (default: ASSIGNMENT)'),
25
+ state: workState.optional().describe('State'),
26
+ maxPoints: z.number().optional().describe('Max points'),
27
+ due: z.string().optional().describe('Due datetime (combined date+time)'),
28
+ dueDate: z.string().optional().describe('Due date (YYYY-MM-DD)'),
29
+ dueTime: z.string().optional().describe('Due time (HH:MM)'),
30
+ scheduled: z.string().optional().describe('Scheduled publish time'),
31
+ topic: z.string().optional().describe('Topic ID'),
32
+ };
33
+
34
+ export function registerExtraClassroomTools(server: McpServer): void {
35
+ server.registerTool('gog_classroom_courses_create', {
36
+ description: 'Create a new Google Classroom course.',
37
+ inputSchema: {
38
+ name: z.string().describe('Course name'),
39
+ ...courseSharedFields,
40
+ account: accountParam,
41
+ },
42
+ }, async ({ name, owner, section, descriptionHeading, description, room, state, account }) => {
43
+ const args = ['classroom', 'courses', 'create', `--name=${name}`];
44
+ if (owner) args.push(`--owner=${owner}`);
45
+ if (section) args.push(`--section=${section}`);
46
+ if (descriptionHeading) args.push(`--description-heading=${descriptionHeading}`);
47
+ if (description) args.push(`--description=${description}`);
48
+ if (room) args.push(`--room=${room}`);
49
+ if (state) args.push(`--state=${state}`);
50
+ return runOrDiagnose(args, { account });
51
+ });
52
+
53
+ server.registerTool('gog_classroom_courses_update', {
54
+ description: 'Update an existing Google Classroom course.',
55
+ annotations: { destructiveHint: true },
56
+ inputSchema: {
57
+ courseId: z.string().describe('Course ID'),
58
+ name: z.string().optional().describe('Course name'),
59
+ ...courseSharedFields,
60
+ account: accountParam,
61
+ },
62
+ }, async ({ courseId, name, owner, section, descriptionHeading, description, room, state, account }) => {
63
+ const args = ['classroom', 'courses', 'update', courseId];
64
+ if (name) args.push(`--name=${name}`);
65
+ if (owner) args.push(`--owner=${owner}`);
66
+ if (section) args.push(`--section=${section}`);
67
+ if (descriptionHeading) args.push(`--description-heading=${descriptionHeading}`);
68
+ if (description) args.push(`--description=${description}`);
69
+ if (room) args.push(`--room=${room}`);
70
+ if (state) args.push(`--state=${state}`);
71
+ return runOrDiagnose(args, { account });
72
+ });
73
+
74
+ server.registerTool('gog_classroom_courses_delete', {
75
+ description: 'Delete a Google Classroom course.',
76
+ annotations: { destructiveHint: true },
77
+ inputSchema: {
78
+ courseId: z.string().describe('Course ID'),
79
+ account: accountParam,
80
+ },
81
+ }, async ({ courseId, account }) => {
82
+ return runOrDiagnose(['classroom', 'courses', 'delete', courseId], { account });
83
+ });
84
+
85
+ server.registerTool('gog_classroom_courses_archive', {
86
+ description: 'Archive a Google Classroom course.',
87
+ annotations: { destructiveHint: true },
88
+ inputSchema: {
89
+ courseId: z.string().describe('Course ID'),
90
+ account: accountParam,
91
+ },
92
+ }, async ({ courseId, account }) => {
93
+ return runOrDiagnose(['classroom', 'courses', 'archive', courseId], { account });
94
+ });
95
+
96
+ server.registerTool('gog_classroom_courses_unarchive', {
97
+ description: 'Unarchive a Google Classroom course (restore to ACTIVE).',
98
+ inputSchema: {
99
+ courseId: z.string().describe('Course ID'),
100
+ account: accountParam,
101
+ },
102
+ }, async ({ courseId, account }) => {
103
+ return runOrDiagnose(['classroom', 'courses', 'unarchive', courseId], { account });
104
+ });
105
+
106
+ server.registerTool('gog_classroom_students_add', {
107
+ description: 'Add a student to a Google Classroom course.',
108
+ inputSchema: {
109
+ courseId: z.string().describe('Course ID'),
110
+ userId: z.string().describe('Student user ID (or "me")'),
111
+ enrollmentCode: z.string().optional().describe('Enrollment code (required if adding self via code)'),
112
+ account: accountParam,
113
+ },
114
+ }, async ({ courseId, userId, enrollmentCode, account }) => {
115
+ const args = ['classroom', 'students', 'add', courseId, userId];
116
+ if (enrollmentCode) args.push(`--enrollment-code=${enrollmentCode}`);
117
+ return runOrDiagnose(args, { account });
118
+ });
119
+
120
+ server.registerTool('gog_classroom_students_remove', {
121
+ description: 'Remove a student from a Google Classroom course.',
122
+ annotations: { destructiveHint: true },
123
+ inputSchema: {
124
+ courseId: z.string().describe('Course ID'),
125
+ userId: z.string().describe('Student user ID'),
126
+ account: accountParam,
127
+ },
128
+ }, async ({ courseId, userId, account }) => {
129
+ return runOrDiagnose(['classroom', 'students', 'remove', courseId, userId], { account });
130
+ });
131
+
132
+ server.registerTool('gog_classroom_teachers_add', {
133
+ description: 'Add a teacher to a Google Classroom course.',
134
+ inputSchema: {
135
+ courseId: z.string().describe('Course ID'),
136
+ userId: z.string().describe('Teacher user ID'),
137
+ account: accountParam,
138
+ },
139
+ }, async ({ courseId, userId, account }) => {
140
+ return runOrDiagnose(['classroom', 'teachers', 'add', courseId, userId], { account });
141
+ });
142
+
143
+ server.registerTool('gog_classroom_teachers_remove', {
144
+ description: 'Remove a teacher from a Google Classroom course.',
145
+ annotations: { destructiveHint: true },
146
+ inputSchema: {
147
+ courseId: z.string().describe('Course ID'),
148
+ userId: z.string().describe('Teacher user ID'),
149
+ account: accountParam,
150
+ },
151
+ }, async ({ courseId, userId, account }) => {
152
+ return runOrDiagnose(['classroom', 'teachers', 'remove', courseId, userId], { account });
153
+ });
154
+
155
+ server.registerTool('gog_classroom_coursework_create', {
156
+ description: 'Create a new coursework item (assignment, question, etc.) in a course.',
157
+ inputSchema: {
158
+ courseId: z.string().describe('Course ID'),
159
+ title: z.string().describe('Coursework title'),
160
+ ...courseworkSharedFields,
161
+ account: accountParam,
162
+ },
163
+ }, async ({ courseId, title, description, type, state, maxPoints, due, dueDate, dueTime, scheduled, topic, account }) => {
164
+ const args = ['classroom', 'coursework', 'create', courseId, `--title=${title}`];
165
+ if (description) args.push(`--description=${description}`);
166
+ if (type) args.push(`--type=${type}`);
167
+ if (state) args.push(`--state=${state}`);
168
+ if (maxPoints !== undefined) args.push(`--max-points=${maxPoints}`);
169
+ if (due) args.push(`--due=${due}`);
170
+ if (dueDate) args.push(`--due-date=${dueDate}`);
171
+ if (dueTime) args.push(`--due-time=${dueTime}`);
172
+ if (scheduled) args.push(`--scheduled=${scheduled}`);
173
+ if (topic) args.push(`--topic=${topic}`);
174
+ return runOrDiagnose(args, { account });
175
+ });
176
+
177
+ server.registerTool('gog_classroom_coursework_update', {
178
+ description: 'Update an existing coursework item.',
179
+ annotations: { destructiveHint: true },
180
+ inputSchema: {
181
+ courseId: z.string().describe('Course ID'),
182
+ courseworkId: z.string().describe('Coursework ID'),
183
+ title: z.string().optional().describe('New title'),
184
+ ...courseworkSharedFields,
185
+ account: accountParam,
186
+ },
187
+ }, async ({ courseId, courseworkId, title, description, type, state, maxPoints, due, dueDate, dueTime, scheduled, topic, account }) => {
188
+ const args = ['classroom', 'coursework', 'update', courseId, courseworkId];
189
+ if (title) args.push(`--title=${title}`);
190
+ if (description) args.push(`--description=${description}`);
191
+ if (type) args.push(`--type=${type}`);
192
+ if (state) args.push(`--state=${state}`);
193
+ if (maxPoints !== undefined) args.push(`--max-points=${maxPoints}`);
194
+ if (due) args.push(`--due=${due}`);
195
+ if (dueDate) args.push(`--due-date=${dueDate}`);
196
+ if (dueTime) args.push(`--due-time=${dueTime}`);
197
+ if (scheduled) args.push(`--scheduled=${scheduled}`);
198
+ if (topic) args.push(`--topic=${topic}`);
199
+ return runOrDiagnose(args, { account });
200
+ });
201
+
202
+ server.registerTool('gog_classroom_coursework_delete', {
203
+ description: 'Delete a coursework item.',
204
+ annotations: { destructiveHint: true },
205
+ inputSchema: {
206
+ courseId: z.string().describe('Course ID'),
207
+ courseworkId: z.string().describe('Coursework ID'),
208
+ account: accountParam,
209
+ },
210
+ }, async ({ courseId, courseworkId, account }) => {
211
+ return runOrDiagnose(['classroom', 'coursework', 'delete', courseId, courseworkId], { account });
212
+ });
213
+
214
+ server.registerTool('gog_classroom_announcements_update', {
215
+ description: 'Update an existing announcement.',
216
+ annotations: { destructiveHint: true },
217
+ inputSchema: {
218
+ courseId: z.string().describe('Course ID'),
219
+ announcementId: z.string().describe('Announcement ID'),
220
+ text: z.string().optional().describe('New text'),
221
+ state: workState.optional().describe('State'),
222
+ scheduled: z.string().optional().describe('Scheduled publish time'),
223
+ account: accountParam,
224
+ },
225
+ }, async ({ courseId, announcementId, text, state, scheduled, account }) => {
226
+ const args = ['classroom', 'announcements', 'update', courseId, announcementId];
227
+ if (text) args.push(`--text=${text}`);
228
+ if (state) args.push(`--state=${state}`);
229
+ if (scheduled) args.push(`--scheduled=${scheduled}`);
230
+ return runOrDiagnose(args, { account });
231
+ });
232
+
233
+ server.registerTool('gog_classroom_announcements_delete', {
234
+ description: 'Delete an announcement.',
235
+ annotations: { destructiveHint: true },
236
+ inputSchema: {
237
+ courseId: z.string().describe('Course ID'),
238
+ announcementId: z.string().describe('Announcement ID'),
239
+ account: accountParam,
240
+ },
241
+ }, async ({ courseId, announcementId, account }) => {
242
+ return runOrDiagnose(['classroom', 'announcements', 'delete', courseId, announcementId], { account });
243
+ });
244
+
245
+ server.registerTool('gog_classroom_topics_create', {
246
+ description: 'Create a topic in a Google Classroom course.',
247
+ inputSchema: {
248
+ courseId: z.string().describe('Course ID'),
249
+ name: z.string().describe('Topic name'),
250
+ account: accountParam,
251
+ },
252
+ }, async ({ courseId, name, account }) => {
253
+ return runOrDiagnose(['classroom', 'topics', 'create', courseId, `--name=${name}`], { account });
254
+ });
255
+
256
+ server.registerTool('gog_classroom_topics_update', {
257
+ description: 'Rename an existing topic.',
258
+ annotations: { destructiveHint: true },
259
+ inputSchema: {
260
+ courseId: z.string().describe('Course ID'),
261
+ topicId: z.string().describe('Topic ID'),
262
+ name: z.string().describe('New topic name'),
263
+ account: accountParam,
264
+ },
265
+ }, async ({ courseId, topicId, name, account }) => {
266
+ return runOrDiagnose(['classroom', 'topics', 'update', courseId, topicId, `--name=${name}`], { account });
267
+ });
268
+
269
+ server.registerTool('gog_classroom_topics_delete', {
270
+ description: 'Delete a topic.',
271
+ annotations: { destructiveHint: true },
272
+ inputSchema: {
273
+ courseId: z.string().describe('Course ID'),
274
+ topicId: z.string().describe('Topic ID'),
275
+ account: accountParam,
276
+ },
277
+ }, async ({ courseId, topicId, account }) => {
278
+ return runOrDiagnose(['classroom', 'topics', 'delete', courseId, topicId], { account });
279
+ });
280
+
281
+ server.registerTool('gog_classroom_invitations_create', {
282
+ description: 'Create an invitation to a Google Classroom course.',
283
+ inputSchema: {
284
+ courseId: z.string().describe('Course ID'),
285
+ userId: z.string().describe('User ID to invite'),
286
+ role: z.enum(['STUDENT', 'TEACHER', 'OWNER']).describe('Role for the invited user'),
287
+ account: accountParam,
288
+ },
289
+ }, async ({ courseId, userId, role, account }) => {
290
+ return runOrDiagnose(['classroom', 'invitations', 'create', courseId, userId, `--role=${role}`], { account });
291
+ });
292
+
293
+ server.registerTool('gog_classroom_invitations_delete', {
294
+ description: 'Delete (revoke) a Google Classroom invitation.',
295
+ annotations: { destructiveHint: true },
296
+ inputSchema: {
297
+ invitationId: z.string().describe('Invitation ID'),
298
+ account: accountParam,
299
+ },
300
+ }, async ({ invitationId, account }) => {
301
+ return runOrDiagnose(['classroom', 'invitations', 'delete', invitationId], { account });
302
+ });
5
303
  }
@@ -1,12 +1,264 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
3
2
  import { registerExtraClassroomTools } from '../../src/tools/classroom-extra.js';
3
+ import * as lib from '../../../gogcli-mcp/src/lib.js';
4
+ import { setupExtrasHandlers, toText, type ToolHandler } from '../../../gogcli-mcp/tests/helpers/extras-harness.js';
4
5
 
5
- describe('registerExtraClassroomTools', () => {
6
- it('registers no tools (no extras yet)', () => {
7
- const server = new McpServer({ name: 'test', version: '0.0.0' });
8
- const spy = vi.spyOn(server, 'registerTool');
9
- registerExtraClassroomTools(server);
10
- expect(spy).not.toHaveBeenCalled();
6
+ vi.mock('../../../gogcli-mcp/src/lib.js', async (importOriginal) => {
7
+ const actual = await importOriginal<typeof lib>();
8
+ return {
9
+ ...actual,
10
+ runOrDiagnose: vi.fn(),
11
+ };
12
+ });
13
+
14
+ let handlers: Map<string, ToolHandler>;
15
+
16
+ beforeEach(() => {
17
+ vi.clearAllMocks();
18
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
19
+ handlers = setupExtrasHandlers(registerExtraClassroomTools);
20
+ });
21
+
22
+ describe('gog_classroom_courses_create', () => {
23
+ it('calls runOrDiagnose with required name only', async () => {
24
+ await handlers.get('gog_classroom_courses_create')!({ name: 'Math 101' });
25
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
26
+ ['classroom', 'courses', 'create', '--name=Math 101'],
27
+ { account: undefined },
28
+ );
29
+ });
30
+
31
+ it('passes all optional flags', async () => {
32
+ await handlers.get('gog_classroom_courses_create')!({
33
+ name: 'Math 101',
34
+ owner: 'me',
35
+ section: 'Section A',
36
+ descriptionHeading: 'Welcome',
37
+ description: 'Algebra',
38
+ room: 'R101',
39
+ state: 'ACTIVE',
40
+ });
41
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
42
+ ['classroom', 'courses', 'create', '--name=Math 101', '--owner=me', '--section=Section A', '--description-heading=Welcome', '--description=Algebra', '--room=R101', '--state=ACTIVE'],
43
+ { account: undefined },
44
+ );
45
+ });
46
+ });
47
+
48
+ describe('gog_classroom_courses_update', () => {
49
+ it('calls runOrDiagnose with courseId only', async () => {
50
+ await handlers.get('gog_classroom_courses_update')!({ courseId: 'c1' });
51
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['classroom', 'courses', 'update', 'c1'], { account: undefined });
52
+ });
53
+
54
+ it('passes all optional flags', async () => {
55
+ await handlers.get('gog_classroom_courses_update')!({
56
+ courseId: 'c1',
57
+ name: 'New Name',
58
+ owner: 'me',
59
+ section: 'B',
60
+ descriptionHeading: 'Heading',
61
+ description: 'Desc',
62
+ room: 'R2',
63
+ state: 'ARCHIVED',
64
+ });
65
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
66
+ ['classroom', 'courses', 'update', 'c1', '--name=New Name', '--owner=me', '--section=B', '--description-heading=Heading', '--description=Desc', '--room=R2', '--state=ARCHIVED'],
67
+ { account: undefined },
68
+ );
69
+ });
70
+ });
71
+
72
+ describe('gog_classroom_courses_delete', () => {
73
+ it('calls runOrDiagnose with courseId', async () => {
74
+ await handlers.get('gog_classroom_courses_delete')!({ courseId: 'c1' });
75
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['classroom', 'courses', 'delete', 'c1'], { account: undefined });
76
+ });
77
+ });
78
+
79
+ describe('gog_classroom_courses_archive', () => {
80
+ it('calls runOrDiagnose with courseId', async () => {
81
+ await handlers.get('gog_classroom_courses_archive')!({ courseId: 'c1' });
82
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['classroom', 'courses', 'archive', 'c1'], { account: undefined });
83
+ });
84
+ });
85
+
86
+ describe('gog_classroom_courses_unarchive', () => {
87
+ it('calls runOrDiagnose with courseId', async () => {
88
+ await handlers.get('gog_classroom_courses_unarchive')!({ courseId: 'c1' });
89
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['classroom', 'courses', 'unarchive', 'c1'], { account: undefined });
90
+ });
91
+ });
92
+
93
+ describe('gog_classroom_students_add', () => {
94
+ it('calls runOrDiagnose with courseId and userId', async () => {
95
+ await handlers.get('gog_classroom_students_add')!({ courseId: 'c1', userId: 'u1' });
96
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['classroom', 'students', 'add', 'c1', 'u1'], { account: undefined });
97
+ });
98
+
99
+ it('passes --enrollment-code when provided', async () => {
100
+ await handlers.get('gog_classroom_students_add')!({ courseId: 'c1', userId: 'u1', enrollmentCode: 'abc123' });
101
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
102
+ ['classroom', 'students', 'add', 'c1', 'u1', '--enrollment-code=abc123'],
103
+ { account: undefined },
104
+ );
105
+ });
106
+ });
107
+
108
+ describe('gog_classroom_students_remove', () => {
109
+ it('calls runOrDiagnose with courseId and userId', async () => {
110
+ await handlers.get('gog_classroom_students_remove')!({ courseId: 'c1', userId: 'u1' });
111
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['classroom', 'students', 'remove', 'c1', 'u1'], { account: undefined });
112
+ });
113
+ });
114
+
115
+ describe('gog_classroom_teachers_add', () => {
116
+ it('calls runOrDiagnose with courseId and userId', async () => {
117
+ await handlers.get('gog_classroom_teachers_add')!({ courseId: 'c1', userId: 'u1' });
118
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['classroom', 'teachers', 'add', 'c1', 'u1'], { account: undefined });
119
+ });
120
+ });
121
+
122
+ describe('gog_classroom_teachers_remove', () => {
123
+ it('calls runOrDiagnose with courseId and userId', async () => {
124
+ await handlers.get('gog_classroom_teachers_remove')!({ courseId: 'c1', userId: 'u1' });
125
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['classroom', 'teachers', 'remove', 'c1', 'u1'], { account: undefined });
126
+ });
127
+ });
128
+
129
+ describe('gog_classroom_coursework_create', () => {
130
+ it('calls runOrDiagnose with required title only', async () => {
131
+ await handlers.get('gog_classroom_coursework_create')!({ courseId: 'c1', title: 'HW1' });
132
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
133
+ ['classroom', 'coursework', 'create', 'c1', '--title=HW1'],
134
+ { account: undefined },
135
+ );
136
+ });
137
+
138
+ it('passes all optional flags', async () => {
139
+ await handlers.get('gog_classroom_coursework_create')!({
140
+ courseId: 'c1',
141
+ title: 'HW1',
142
+ description: 'Chapter 1',
143
+ type: 'ASSIGNMENT',
144
+ state: 'PUBLISHED',
145
+ maxPoints: 100,
146
+ due: '2026-05-01T23:59',
147
+ dueDate: '2026-05-01',
148
+ dueTime: '23:59',
149
+ scheduled: '2026-04-30T12:00',
150
+ topic: 't1',
151
+ });
152
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
153
+ ['classroom', 'coursework', 'create', 'c1', '--title=HW1', '--description=Chapter 1', '--type=ASSIGNMENT', '--state=PUBLISHED', '--max-points=100', '--due=2026-05-01T23:59', '--due-date=2026-05-01', '--due-time=23:59', '--scheduled=2026-04-30T12:00', '--topic=t1'],
154
+ { account: undefined },
155
+ );
156
+ });
157
+ });
158
+
159
+ describe('gog_classroom_coursework_update', () => {
160
+ it('calls runOrDiagnose with ids only', async () => {
161
+ await handlers.get('gog_classroom_coursework_update')!({ courseId: 'c1', courseworkId: 'w1' });
162
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['classroom', 'coursework', 'update', 'c1', 'w1'], { account: undefined });
163
+ });
164
+
165
+ it('passes all optional flags', async () => {
166
+ await handlers.get('gog_classroom_coursework_update')!({
167
+ courseId: 'c1',
168
+ courseworkId: 'w1',
169
+ title: 'New Title',
170
+ description: 'Desc',
171
+ type: 'SHORT_ANSWER_QUESTION',
172
+ state: 'DRAFT',
173
+ maxPoints: 50,
174
+ due: '2026-05-01T23:59',
175
+ dueDate: '2026-05-01',
176
+ dueTime: '23:59',
177
+ scheduled: '2026-04-30T12:00',
178
+ topic: 't1',
179
+ });
180
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
181
+ ['classroom', 'coursework', 'update', 'c1', 'w1', '--title=New Title', '--description=Desc', '--type=SHORT_ANSWER_QUESTION', '--state=DRAFT', '--max-points=50', '--due=2026-05-01T23:59', '--due-date=2026-05-01', '--due-time=23:59', '--scheduled=2026-04-30T12:00', '--topic=t1'],
182
+ { account: undefined },
183
+ );
184
+ });
185
+ });
186
+
187
+ describe('gog_classroom_coursework_delete', () => {
188
+ it('calls runOrDiagnose with ids', async () => {
189
+ await handlers.get('gog_classroom_coursework_delete')!({ courseId: 'c1', courseworkId: 'w1' });
190
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['classroom', 'coursework', 'delete', 'c1', 'w1'], { account: undefined });
191
+ });
192
+ });
193
+
194
+ describe('gog_classroom_announcements_update', () => {
195
+ it('calls runOrDiagnose with ids only', async () => {
196
+ await handlers.get('gog_classroom_announcements_update')!({ courseId: 'c1', announcementId: 'a1' });
197
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['classroom', 'announcements', 'update', 'c1', 'a1'], { account: undefined });
198
+ });
199
+
200
+ it('passes all optional flags', async () => {
201
+ await handlers.get('gog_classroom_announcements_update')!({
202
+ courseId: 'c1',
203
+ announcementId: 'a1',
204
+ text: 'edited',
205
+ state: 'PUBLISHED',
206
+ scheduled: '2026-05-01T12:00',
207
+ });
208
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
209
+ ['classroom', 'announcements', 'update', 'c1', 'a1', '--text=edited', '--state=PUBLISHED', '--scheduled=2026-05-01T12:00'],
210
+ { account: undefined },
211
+ );
212
+ });
213
+ });
214
+
215
+ describe('gog_classroom_announcements_delete', () => {
216
+ it('calls runOrDiagnose with ids', async () => {
217
+ await handlers.get('gog_classroom_announcements_delete')!({ courseId: 'c1', announcementId: 'a1' });
218
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['classroom', 'announcements', 'delete', 'c1', 'a1'], { account: undefined });
219
+ });
220
+ });
221
+
222
+ describe('gog_classroom_topics_create', () => {
223
+ it('calls runOrDiagnose with courseId and name', async () => {
224
+ await handlers.get('gog_classroom_topics_create')!({ courseId: 'c1', name: 'Week 1' });
225
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
226
+ ['classroom', 'topics', 'create', 'c1', '--name=Week 1'],
227
+ { account: undefined },
228
+ );
229
+ });
230
+ });
231
+
232
+ describe('gog_classroom_topics_update', () => {
233
+ it('calls runOrDiagnose with ids and name', async () => {
234
+ await handlers.get('gog_classroom_topics_update')!({ courseId: 'c1', topicId: 't1', name: 'Week 2' });
235
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
236
+ ['classroom', 'topics', 'update', 'c1', 't1', '--name=Week 2'],
237
+ { account: undefined },
238
+ );
239
+ });
240
+ });
241
+
242
+ describe('gog_classroom_topics_delete', () => {
243
+ it('calls runOrDiagnose with ids', async () => {
244
+ await handlers.get('gog_classroom_topics_delete')!({ courseId: 'c1', topicId: 't1' });
245
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['classroom', 'topics', 'delete', 'c1', 't1'], { account: undefined });
246
+ });
247
+ });
248
+
249
+ describe('gog_classroom_invitations_create', () => {
250
+ it('calls runOrDiagnose with courseId, userId, role', async () => {
251
+ await handlers.get('gog_classroom_invitations_create')!({ courseId: 'c1', userId: 'u1', role: 'STUDENT' });
252
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
253
+ ['classroom', 'invitations', 'create', 'c1', 'u1', '--role=STUDENT'],
254
+ { account: undefined },
255
+ );
256
+ });
257
+ });
258
+
259
+ describe('gog_classroom_invitations_delete', () => {
260
+ it('calls runOrDiagnose with invitationId', async () => {
261
+ await handlers.get('gog_classroom_invitations_delete')!({ invitationId: 'i1' });
262
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['classroom', 'invitations', 'delete', 'i1'], { account: undefined });
11
263
  });
12
264
  });