gogcli-mcp-classroom 2.23.1 → 2.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "Extended Google Classroom for Claude via gogcli — auth + full Classroom support (courses, rosters, coursework, submissions, announcements, topics, invitations)",
10
- "version": "2.23.1"
10
+ "version": "2.24.0"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -15,7 +15,7 @@
15
15
  "displayName": "gogcli (Classroom)",
16
16
  "source": "./",
17
17
  "description": "Extended Google Classroom for Claude via gogcli — auth + full Classroom support (courses, rosters, coursework, submissions, announcements, topics, invitations)",
18
- "version": "2.23.1",
18
+ "version": "2.24.0",
19
19
  "author": {
20
20
  "name": "Chris Hall"
21
21
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gogcli-mcp-classroom",
3
3
  "displayName": "gogcli (Classroom)",
4
- "version": "2.23.1",
4
+ "version": "2.24.0",
5
5
  "description": "Extended Google Classroom for Claude via gogcli — auth + full Classroom support (courses, rosters, coursework, submissions, announcements, topics, invitations)",
6
6
  "author": {
7
7
  "name": "Chris Hall",
package/dist/index.js CHANGED
@@ -31542,6 +31542,26 @@ function normalizeTimestamps(text, tz = displayTimeZone(), naiveTz = naiveSource
31542
31542
  return JSON.stringify(parsed, null, detectIndent(text));
31543
31543
  }
31544
31544
 
31545
+ // ../gogcli-mcp/src/pagination.ts
31546
+ function detectIndent2(text) {
31547
+ const match = /\n(\s+)\S/.exec(text);
31548
+ return match ? match[1].replace(/\t/g, " ").length : 0;
31549
+ }
31550
+ function stripConsumedPageToken(text) {
31551
+ const trimmed = text.trim();
31552
+ if (trimmed === "" || !trimmed.startsWith("{")) return text;
31553
+ let parsed;
31554
+ try {
31555
+ parsed = JSON.parse(trimmed);
31556
+ } catch {
31557
+ return text;
31558
+ }
31559
+ const obj = parsed;
31560
+ if (obj.nextPageToken !== "") return text;
31561
+ delete obj.nextPageToken;
31562
+ return JSON.stringify(obj, null, detectIndent2(text));
31563
+ }
31564
+
31545
31565
  // ../gogcli-mcp/src/tools/utils.ts
31546
31566
  var accountParam = external_exports.string().optional().describe(
31547
31567
  "Google account email to use, e.g. you@gmail.com \u2014 must be the full address, not a bare username. Overrides the GOG_ACCOUNT env var. Omit to use the single configured account."
@@ -31570,9 +31590,19 @@ var ids = {
31570
31590
  // People API uses fully-qualified resource names ("people/c123") not bare IDs.
31571
31591
  person: external_exports.string().describe("Person resource name (people/...) or email")
31572
31592
  };
31593
+ var pageTokenParam = external_exports.string().optional().describe(
31594
+ "Cursor for the NEXT page. Pass back the nextPageToken from a previous response verbatim, keeping the query and max identical: call once, then call again with pageToken=<that value>. A response with NO nextPageToken is the last page."
31595
+ );
31596
+ var pageAliasParam = external_exports.string().optional().describe(
31597
+ "Deprecated alias for pageToken, accepted so existing callers keep working. Use pageToken \u2014 it matches the nextPageToken field in the response."
31598
+ );
31599
+ function resolvePageToken(p) {
31600
+ return p.pageToken ?? p.page;
31601
+ }
31573
31602
  var paginationParams = {
31574
31603
  max: external_exports.number().int().optional().describe("Max results"),
31575
- page: external_exports.string().optional().describe("Page token"),
31604
+ pageToken: pageTokenParam,
31605
+ page: pageAliasParam,
31576
31606
  all: external_exports.boolean().optional().describe("Fetch all pages")
31577
31607
  };
31578
31608
  function registerRunTool(server, options) {
@@ -31647,7 +31677,7 @@ ${accounts || "(none)"}${hint}`);
31647
31677
  async function runOrDiagnose(args, options) {
31648
31678
  try {
31649
31679
  const raw = await run(args, options);
31650
- return rawTextResult(options.lossless ? raw : normalizeTimestamps(raw));
31680
+ return rawTextResult(options.lossless ? raw : stripConsumedPageToken(normalizeTimestamps(raw)));
31651
31681
  } catch (err) {
31652
31682
  return diagnose(err);
31653
31683
  }
@@ -31697,6 +31727,7 @@ function formatAuthHealth(raw, now) {
31697
31727
  // ../gogcli-mcp/src/tools/auth.ts
31698
31728
  function registerAuthToolsWith(server, defaultServices) {
31699
31729
  const servicesDescribe = `Services to authorize: "all" or comma-separated list (e.g. "sheets,gmail,calendar"). Default: "${defaultServices}". Prefer the narrowest set you need \u2014 requesting a service whose Google API is not enabled on the OAuth client's project makes Google reject the WHOLE request with invalid_scope.`;
31730
+ const extraScopesDescribe = "Additional raw OAuth scope URIs to request, comma-separated, on top of the ones `services` implies. Use for scopes no service covers \u2014 e.g. https://www.googleapis.com/auth/bigquery.readonly, required before gog_sheets_datasource_* can read BigQuery-backed Connected Sheets. Leave unset otherwise: an extra scope whose API is not enabled on the OAuth client project makes Google reject the WHOLE authorization with invalid_scope.";
31700
31731
  server.registerTool("gog_auth_list", {
31701
31732
  description: "List the Google accounts stored in gogcli, with their scopes. This reads local configuration only \u2014 it does not contact Google and does NOT tell you whether an account still works: a signed-out account whose refresh token expired or was revoked is listed here exactly like a healthy one, scopes and all. Use gog_auth_health to check whether an account can actually authenticate.",
31702
31733
  annotations: { readOnlyHint: true },
@@ -31746,11 +31777,14 @@ function registerAuthToolsWith(server, defaultServices) {
31746
31777
  annotations: { destructiveHint: true },
31747
31778
  inputSchema: {
31748
31779
  email: external_exports.string().describe("Google account email to authorize"),
31749
- services: external_exports.string().optional().default(defaultServices).describe(servicesDescribe)
31780
+ services: external_exports.string().optional().default(defaultServices).describe(servicesDescribe),
31781
+ extraScopes: external_exports.string().optional().describe(extraScopesDescribe)
31750
31782
  }
31751
- }, async ({ email: email3, services = defaultServices }) => {
31783
+ }, async ({ email: email3, services = defaultServices, extraScopes }) => {
31752
31784
  try {
31753
- return rawTextResult(await run(["auth", "add", email3, "--services", services], {
31785
+ const args = ["auth", "add", email3, "--services", services];
31786
+ if (extraScopes) args.push(`--extra-scopes=${extraScopes}`, "--force-consent");
31787
+ return rawTextResult(await run(args, {
31754
31788
  interactive: true,
31755
31789
  timeout: 3e5
31756
31790
  }));
@@ -31762,14 +31796,14 @@ function registerAuthToolsWith(server, defaultServices) {
31762
31796
  description: "Begin REMOTE/headless Google authorization (step 1 of 2). Returns a sign-in URL to open in any browser \u2014 no local server or terminal on the gogcli host is needed, so this works over the hosted connector where the interactive gog_auth_add cannot. Hand the URL to the user; after they sign in, the browser is redirected to a localhost URL that fails to load \u2014 that is expected. They copy that full redirected URL (from the address bar) and you pass it to gog_auth_add_complete. The link is valid for 10 minutes. If you pass a custom `services` here, pass the SAME value to gog_auth_add_complete or the second step will not match this one.",
31763
31797
  inputSchema: {
31764
31798
  email: external_exports.string().describe("Google account email to authorize"),
31765
- services: external_exports.string().optional().default(defaultServices).describe(servicesDescribe)
31799
+ services: external_exports.string().optional().default(defaultServices).describe(servicesDescribe),
31800
+ extraScopes: external_exports.string().optional().describe(`${extraScopesDescribe} Pass the SAME value to gog_auth_add_complete.`)
31766
31801
  }
31767
- }, async ({ email: email3, services = defaultServices }) => {
31802
+ }, async ({ email: email3, services = defaultServices, extraScopes }) => {
31768
31803
  try {
31769
- return rawTextResult(await run(
31770
- ["auth", "add", email3, "--remote", "--step", "1", "--services", services, "--force-consent"],
31771
- { redactMode: "tokens" }
31772
- ));
31804
+ const args = ["auth", "add", email3, "--remote", "--step", "1", "--services", services, "--force-consent"];
31805
+ if (extraScopes) args.push(`--extra-scopes=${extraScopes}`);
31806
+ return rawTextResult(await run(args, { redactMode: "tokens" }));
31773
31807
  } catch (err) {
31774
31808
  return errorResult(errorText(err));
31775
31809
  }
@@ -31784,25 +31818,28 @@ function registerAuthToolsWith(server, defaultServices) {
31784
31818
  ),
31785
31819
  services: external_exports.string().optional().default(defaultServices).describe(
31786
31820
  `Services authorized \u2014 MUST match the value passed to gog_auth_add_url. Default: "${defaultServices}".`
31821
+ ),
31822
+ extraScopes: external_exports.string().optional().describe(
31823
+ "Extra OAuth scope URIs \u2014 MUST match the value passed to gog_auth_add_url, for the same reason `services` must: the two steps have to describe the same grant."
31787
31824
  )
31788
31825
  }
31789
- }, async ({ email: email3, redirectUrl, services = defaultServices }) => {
31826
+ }, async ({ email: email3, redirectUrl, services = defaultServices, extraScopes }) => {
31790
31827
  try {
31791
- return rawTextResult(await run(
31792
- [
31793
- "auth",
31794
- "add",
31795
- email3,
31796
- "--remote",
31797
- "--step",
31798
- "2",
31799
- "--auth-url",
31800
- redirectUrl,
31801
- "--services",
31802
- services,
31803
- "--force-consent"
31804
- ]
31805
- ));
31828
+ const args = [
31829
+ "auth",
31830
+ "add",
31831
+ email3,
31832
+ "--remote",
31833
+ "--step",
31834
+ "2",
31835
+ "--auth-url",
31836
+ redirectUrl,
31837
+ "--services",
31838
+ services,
31839
+ "--force-consent"
31840
+ ];
31841
+ if (extraScopes) args.push(`--extra-scopes=${extraScopes}`);
31842
+ return rawTextResult(await run(args));
31806
31843
  } catch (err) {
31807
31844
  return errorResult(errorText(err));
31808
31845
  }
@@ -31828,17 +31865,19 @@ function registerClassroomTools(server) {
31828
31865
  teacher: external_exports.string().optional().describe("Filter by teacher user ID"),
31829
31866
  student: external_exports.string().optional().describe("Filter by student user ID"),
31830
31867
  max: external_exports.number().optional().describe("Max results per page"),
31831
- page: external_exports.string().optional().describe("Page token for pagination"),
31868
+ pageToken: pageTokenParam,
31869
+ page: pageAliasParam,
31832
31870
  all: external_exports.boolean().optional().describe("Fetch all pages"),
31833
31871
  account: accountParam
31834
31872
  }
31835
- }, async ({ state, teacher, student, max, page, all, account }) => {
31873
+ }, async ({ state, teacher, student, max, pageToken, page, all, account }) => {
31836
31874
  const args = ["classroom", "courses", "list"];
31837
31875
  if (state) args.push(`--state=${state}`);
31838
31876
  if (teacher) args.push(`--teacher=${teacher}`);
31839
31877
  if (student) args.push(`--student=${student}`);
31840
31878
  if (max !== void 0) args.push(`--max=${max}`);
31841
- if (page) args.push(`--page=${page}`);
31879
+ const token = resolvePageToken({ pageToken, page });
31880
+ if (token) args.push(`--page=${token}`);
31842
31881
  if (all) args.push("--all");
31843
31882
  return runOrDiagnose(args, { account });
31844
31883
  });
@@ -31858,14 +31897,16 @@ function registerClassroomTools(server) {
31858
31897
  inputSchema: {
31859
31898
  courseId: external_exports.string().describe("Course ID"),
31860
31899
  max: external_exports.number().optional().describe("Max results per page"),
31861
- page: external_exports.string().optional().describe("Page token"),
31900
+ pageToken: pageTokenParam,
31901
+ page: pageAliasParam,
31862
31902
  all: external_exports.boolean().optional().describe("Fetch all pages"),
31863
31903
  account: accountParam
31864
31904
  }
31865
- }, async ({ courseId, max, page, all, account }) => {
31905
+ }, async ({ courseId, max, pageToken, page, all, account }) => {
31866
31906
  const args = ["classroom", "students", "list", courseId];
31867
31907
  if (max !== void 0) args.push(`--max=${max}`);
31868
- if (page) args.push(`--page=${page}`);
31908
+ const token = resolvePageToken({ pageToken, page });
31909
+ if (token) args.push(`--page=${token}`);
31869
31910
  if (all) args.push("--all");
31870
31911
  return runOrDiagnose(args, { account });
31871
31912
  });
@@ -31886,14 +31927,16 @@ function registerClassroomTools(server) {
31886
31927
  inputSchema: {
31887
31928
  courseId: external_exports.string().describe("Course ID"),
31888
31929
  max: external_exports.number().optional().describe("Max results per page"),
31889
- page: external_exports.string().optional().describe("Page token"),
31930
+ pageToken: pageTokenParam,
31931
+ page: pageAliasParam,
31890
31932
  all: external_exports.boolean().optional().describe("Fetch all pages"),
31891
31933
  account: accountParam
31892
31934
  }
31893
- }, async ({ courseId, max, page, all, account }) => {
31935
+ }, async ({ courseId, max, pageToken, page, all, account }) => {
31894
31936
  const args = ["classroom", "teachers", "list", courseId];
31895
31937
  if (max !== void 0) args.push(`--max=${max}`);
31896
- if (page) args.push(`--page=${page}`);
31938
+ const token = resolvePageToken({ pageToken, page });
31939
+ if (token) args.push(`--page=${token}`);
31897
31940
  if (all) args.push("--all");
31898
31941
  return runOrDiagnose(args, { account });
31899
31942
  });
@@ -31916,16 +31959,18 @@ function registerClassroomTools(server) {
31916
31959
  students: external_exports.boolean().optional().describe("Include students only"),
31917
31960
  teachers: external_exports.boolean().optional().describe("Include teachers only"),
31918
31961
  max: external_exports.number().optional().describe("Max results per page"),
31919
- page: external_exports.string().optional().describe("Page token"),
31962
+ pageToken: pageTokenParam,
31963
+ page: pageAliasParam,
31920
31964
  all: external_exports.boolean().optional().describe("Fetch all pages"),
31921
31965
  account: accountParam
31922
31966
  }
31923
- }, async ({ courseId, students, teachers, max, page, all, account }) => {
31967
+ }, async ({ courseId, students, teachers, max, pageToken, page, all, account }) => {
31924
31968
  const args = ["classroom", "roster", courseId];
31925
31969
  if (students) args.push("--students");
31926
31970
  if (teachers) args.push("--teachers");
31927
31971
  if (max !== void 0) args.push(`--max=${max}`);
31928
- if (page) args.push(`--page=${page}`);
31972
+ const token = resolvePageToken({ pageToken, page });
31973
+ if (token) args.push(`--page=${token}`);
31929
31974
  if (all) args.push("--all");
31930
31975
  return runOrDiagnose(args, { account });
31931
31976
  });
@@ -31938,18 +31983,20 @@ function registerClassroomTools(server) {
31938
31983
  topic: external_exports.string().optional().describe("Filter by topic ID"),
31939
31984
  orderBy: external_exports.string().optional().describe('Sort order (e.g. "updateTime desc")'),
31940
31985
  max: external_exports.number().optional().describe("Max results per page"),
31941
- page: external_exports.string().optional().describe("Page token"),
31986
+ pageToken: pageTokenParam,
31987
+ page: pageAliasParam,
31942
31988
  all: external_exports.boolean().optional().describe("Fetch all pages"),
31943
31989
  scanPages: external_exports.number().optional().describe("Max pages to scan when filtering"),
31944
31990
  account: accountParam
31945
31991
  }
31946
- }, async ({ courseId, state, topic, orderBy, max, page, all, scanPages, account }) => {
31992
+ }, async ({ courseId, state, topic, orderBy, max, pageToken, page, all, scanPages, account }) => {
31947
31993
  const args = ["classroom", "coursework", "list", courseId];
31948
31994
  if (state) args.push(`--state=${state}`);
31949
31995
  if (topic) args.push(`--topic=${topic}`);
31950
31996
  if (orderBy) args.push(`--order-by=${orderBy}`);
31951
31997
  if (max !== void 0) args.push(`--max=${max}`);
31952
- if (page) args.push(`--page=${page}`);
31998
+ const token = resolvePageToken({ pageToken, page });
31999
+ if (token) args.push(`--page=${token}`);
31953
32000
  if (all) args.push("--all");
31954
32001
  if (scanPages !== void 0) args.push(`--scan-pages=${scanPages}`);
31955
32002
  return runOrDiagnose(args, { account });
@@ -31975,17 +32022,19 @@ function registerClassroomTools(server) {
31975
32022
  late: external_exports.enum(["late", "not-late"]).optional().describe("Filter by late status"),
31976
32023
  user: external_exports.string().optional().describe("Filter by student user ID"),
31977
32024
  max: external_exports.number().optional().describe("Max results per page"),
31978
- page: external_exports.string().optional().describe("Page token"),
32025
+ pageToken: pageTokenParam,
32026
+ page: pageAliasParam,
31979
32027
  all: external_exports.boolean().optional().describe("Fetch all pages"),
31980
32028
  account: accountParam
31981
32029
  }
31982
- }, async ({ courseId, courseworkId, state, late: late2, user, max, page, all, account }) => {
32030
+ }, async ({ courseId, courseworkId, state, late: late2, user, max, pageToken, page, all, account }) => {
31983
32031
  const args = ["classroom", "submissions", "list", courseId, courseworkId];
31984
32032
  if (state) args.push(`--state=${state}`);
31985
32033
  if (late2) args.push(`--late=${late2}`);
31986
32034
  if (user) args.push(`--user=${user}`);
31987
32035
  if (max !== void 0) args.push(`--max=${max}`);
31988
- if (page) args.push(`--page=${page}`);
32036
+ const token = resolvePageToken({ pageToken, page });
32037
+ if (token) args.push(`--page=${token}`);
31989
32038
  if (all) args.push("--all");
31990
32039
  return runOrDiagnose(args, { account });
31991
32040
  });
@@ -32062,16 +32111,18 @@ function registerClassroomTools(server) {
32062
32111
  state: external_exports.string().optional().describe("Filter by announcement state"),
32063
32112
  orderBy: external_exports.string().optional().describe("Sort order"),
32064
32113
  max: external_exports.number().optional().describe("Max results per page"),
32065
- page: external_exports.string().optional().describe("Page token"),
32114
+ pageToken: pageTokenParam,
32115
+ page: pageAliasParam,
32066
32116
  all: external_exports.boolean().optional().describe("Fetch all pages"),
32067
32117
  account: accountParam
32068
32118
  }
32069
- }, async ({ courseId, state, orderBy, max, page, all, account }) => {
32119
+ }, async ({ courseId, state, orderBy, max, pageToken, page, all, account }) => {
32070
32120
  const args = ["classroom", "announcements", "list", courseId];
32071
32121
  if (state) args.push(`--state=${state}`);
32072
32122
  if (orderBy) args.push(`--order-by=${orderBy}`);
32073
32123
  if (max !== void 0) args.push(`--max=${max}`);
32074
- if (page) args.push(`--page=${page}`);
32124
+ const token = resolvePageToken({ pageToken, page });
32125
+ if (token) args.push(`--page=${token}`);
32075
32126
  if (all) args.push("--all");
32076
32127
  return runOrDiagnose(args, { account });
32077
32128
  });
@@ -32107,14 +32158,16 @@ function registerClassroomTools(server) {
32107
32158
  inputSchema: {
32108
32159
  courseId: external_exports.string().describe("Course ID"),
32109
32160
  max: external_exports.number().optional().describe("Max results per page"),
32110
- page: external_exports.string().optional().describe("Page token"),
32161
+ pageToken: pageTokenParam,
32162
+ page: pageAliasParam,
32111
32163
  all: external_exports.boolean().optional().describe("Fetch all pages"),
32112
32164
  account: accountParam
32113
32165
  }
32114
- }, async ({ courseId, max, page, all, account }) => {
32166
+ }, async ({ courseId, max, pageToken, page, all, account }) => {
32115
32167
  const args = ["classroom", "topics", "list", courseId];
32116
32168
  if (max !== void 0) args.push(`--max=${max}`);
32117
- if (page) args.push(`--page=${page}`);
32169
+ const token = resolvePageToken({ pageToken, page });
32170
+ if (token) args.push(`--page=${token}`);
32118
32171
  if (all) args.push("--all");
32119
32172
  return runOrDiagnose(args, { account });
32120
32173
  });
@@ -32136,16 +32189,18 @@ function registerClassroomTools(server) {
32136
32189
  course: external_exports.string().optional().describe("Filter by course ID"),
32137
32190
  user: external_exports.string().optional().describe("Filter by user ID"),
32138
32191
  max: external_exports.number().optional().describe("Max results per page"),
32139
- page: external_exports.string().optional().describe("Page token"),
32192
+ pageToken: pageTokenParam,
32193
+ page: pageAliasParam,
32140
32194
  all: external_exports.boolean().optional().describe("Fetch all pages"),
32141
32195
  account: accountParam
32142
32196
  }
32143
- }, async ({ course, user, max, page, all, account }) => {
32197
+ }, async ({ course, user, max, pageToken, page, all, account }) => {
32144
32198
  const args = ["classroom", "invitations", "list"];
32145
32199
  if (course) args.push(`--course=${course}`);
32146
32200
  if (user) args.push(`--user=${user}`);
32147
32201
  if (max !== void 0) args.push(`--max=${max}`);
32148
- if (page) args.push(`--page=${page}`);
32202
+ const token = resolvePageToken({ pageToken, page });
32203
+ if (token) args.push(`--page=${token}`);
32149
32204
  if (all) args.push("--all");
32150
32205
  return runOrDiagnose(args, { account });
32151
32206
  });
@@ -32197,7 +32252,7 @@ var failIfNotEmptyParam = external_exports.boolean().optional().describe(
32197
32252
  );
32198
32253
 
32199
32254
  // ../gogcli-mcp/src/server.ts
32200
- var VERSION = true ? "2.23.1" : "0.0.0";
32255
+ var VERSION = true ? "2.24.0" : "0.0.0";
32201
32256
 
32202
32257
  // ../gogcli-mcp/src/auth-log.ts
32203
32258
  var FAILURES = /* @__PURE__ */ new Set([
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.23.1",
6
+ "version": "2.24.0",
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.23.1",
3
+ "version": "2.24.0",
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
@@ -7,12 +7,12 @@
7
7
  "source": "github",
8
8
  "subfolder": "packages/gogcli-mcp-classroom"
9
9
  },
10
- "version": "2.23.1",
10
+ "version": "2.24.0",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "gogcli-mcp-classroom",
15
- "version": "2.23.1",
15
+ "version": "2.24.0",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  },