canvas-lms-mcp 2.0.0 → 2.2.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.
Files changed (44) hide show
  1. package/README.md +5 -5
  2. package/dist/canvas/activity-stream.d.ts +35 -0
  3. package/dist/canvas/activity-stream.d.ts.map +1 -0
  4. package/dist/canvas/client.d.ts +3 -1
  5. package/dist/canvas/client.d.ts.map +1 -1
  6. package/dist/canvas/index.cjs +3 -3
  7. package/dist/canvas/index.d.ts +4 -0
  8. package/dist/canvas/index.d.ts.map +1 -1
  9. package/dist/canvas/index.js +2 -2
  10. package/dist/canvas/planner.d.ts +31 -0
  11. package/dist/canvas/planner.d.ts.map +1 -0
  12. package/dist/canvas/types.d.ts +326 -1
  13. package/dist/canvas/types.d.ts.map +1 -1
  14. package/dist/{chunk-KNYGBEL5.cjs → chunk-C2C3TYWD.cjs} +16 -8
  15. package/dist/chunk-C2C3TYWD.cjs.map +1 -0
  16. package/dist/{chunk-66PSR2DZ.cjs → chunk-EMIO4XD4.cjs} +75 -4
  17. package/dist/chunk-EMIO4XD4.cjs.map +1 -0
  18. package/dist/{chunk-KHJEIVEQ.js → chunk-FKTJGYLG.js} +410 -9
  19. package/dist/chunk-FKTJGYLG.js.map +1 -0
  20. package/dist/{chunk-5B3JDDYC.js → chunk-FP4A34TD.js} +73 -2
  21. package/dist/chunk-FP4A34TD.js.map +1 -0
  22. package/dist/{chunk-AWI5UBRV.cjs → chunk-JR74DYXY.cjs} +805 -404
  23. package/dist/chunk-JR74DYXY.cjs.map +1 -0
  24. package/dist/{chunk-VVIZW7HE.js → chunk-W5DF5YBG.js} +13 -5
  25. package/dist/{chunk-VVIZW7HE.js.map → chunk-W5DF5YBG.js.map} +1 -1
  26. package/dist/http.cjs +6 -6
  27. package/dist/http.js +3 -3
  28. package/dist/init.cjs +3 -3
  29. package/dist/init.js +1 -1
  30. package/dist/provenance/fields.d.ts.map +1 -1
  31. package/dist/pseudonym/coverage.d.ts.map +1 -1
  32. package/dist/pseudonym/pseudonymizer.d.ts +118 -1
  33. package/dist/pseudonym/pseudonymizer.d.ts.map +1 -1
  34. package/dist/server.cjs +4 -4
  35. package/dist/server.js +3 -3
  36. package/dist/stdio.cjs +4 -4
  37. package/dist/stdio.js +3 -3
  38. package/dist/tools/student.d.ts.map +1 -1
  39. package/package.json +2 -2
  40. package/dist/chunk-5B3JDDYC.js.map +0 -1
  41. package/dist/chunk-66PSR2DZ.cjs.map +0 -1
  42. package/dist/chunk-AWI5UBRV.cjs.map +0 -1
  43. package/dist/chunk-KHJEIVEQ.js.map +0 -1
  44. package/dist/chunk-KNYGBEL5.cjs.map +0 -1
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  MCP server for [Canvas LMS](https://www.instructure.com/canvas). Read courses, assignments, submissions, rubrics, quizzes; grade, comment, manage course content, and handle Canvas admin workflows from any AI agent.
13
13
 
14
- 169 tools across Canvas courses, assignments, submissions, gradebook history, rubrics, quizzes, New Quizzes (LTI), files, users, groups, enrollments, discussions, modules, pages, calendar, conversations, peer reviews, accounts, analytics, outcomes, grading standards, grade projection, link audit, accessibility audit, content exports, content migrations, quiz accommodations, appointment groups, student workflows, student search, dashboard, instructor attention workflows, and health checks. Three deployment modes: stdio, HTTP, and library import.
14
+ 172 tools across Canvas courses, assignments, submissions, gradebook history, rubrics, quizzes, New Quizzes (LTI), files, users, groups, enrollments, discussions, modules, pages, calendar, conversations, peer reviews, accounts, analytics, outcomes, grading standards, grade projection, link audit, accessibility audit, content exports, content migrations, quiz accommodations, appointment groups, student workflows, student search, dashboard, instructor attention workflows, and health checks. Three deployment modes: stdio, HTTP, and library import.
15
15
 
16
16
  ## One-click install (Claude Desktop)
17
17
 
@@ -45,7 +45,7 @@ Installs the MCP server (via `npx canvas-lms-mcp`) and all 16 [Agent Skills](#ag
45
45
  | | canvas-lms-mcp | [vishalsachdev/canvas-mcp](https://github.com/vishalsachdev/canvas-mcp) | [DMontgomery40/mcp-canvas-lms](https://github.com/DMontgomery40/mcp-canvas-lms) |
46
46
  |---|---|---|---|
47
47
  | Language | TypeScript | Python | TypeScript |
48
- | Tools | 169 | 80+ | 54 |
48
+ | Tools | 172 | 80+ | 54 |
49
49
  | License | [![License: MIT](https://img.shields.io/github/license/bruchris/canvas-lms-mcp)](https://github.com/bruchris/canvas-lms-mcp/blob/main/LICENSE) | [![License](https://img.shields.io/github/license/vishalsachdev/canvas-mcp)](https://github.com/vishalsachdev/canvas-mcp/blob/main/LICENSE) | [![License](https://img.shields.io/github/license/DMontgomery40/mcp-canvas-lms)](https://github.com/DMontgomery40/mcp-canvas-lms/blob/main/LICENSE) |
50
50
  | Last commit | [![Last commit](https://img.shields.io/github/last-commit/bruchris/canvas-lms-mcp)](https://github.com/bruchris/canvas-lms-mcp) | [![Last commit](https://img.shields.io/github/last-commit/vishalsachdev/canvas-mcp)](https://github.com/vishalsachdev/canvas-mcp) | [![Last commit](https://img.shields.io/github/last-commit/DMontgomery40/mcp-canvas-lms)](https://github.com/DMontgomery40/mcp-canvas-lms) |
51
51
 
@@ -152,7 +152,7 @@ Once configured, try these prompts with your AI client:
152
152
  | Attention | `list_submission_comments_needing_attention`, `list_students_needing_attention` |
153
153
  | FERPA (conditional) | `resolve_pseudonym` — stdio only, registered when `CANVAS_PSEUDONYMIZE_STUDENTS=true` and `CANVAS_PSEUDONYMIZE_REVERSE_LOOKUP=true` |
154
154
 
155
- 117 tools are read-only and 52 tools perform Canvas write operations. When FERPA mode is enabled **on the stdio transport**, `resolve_pseudonym` is registered as the 170th tool overall (118th read tool). The HTTP transport never registers it — see [FERPA mode](#ferpa-mode-student-pseudonymization).
155
+ 120 tools are read-only and 52 tools perform Canvas write operations. When FERPA mode is enabled **on the stdio transport**, `resolve_pseudonym` is registered as the 173rd tool overall (121st read tool). The HTTP transport never registers it — see [FERPA mode](#ferpa-mode-student-pseudonymization).
156
156
 
157
157
  All write tools require appropriate Canvas permissions. Canvas enforces its own permission model -- the MCP server does not bypass it.
158
158
 
@@ -545,8 +545,8 @@ Three roles, plus the default of "unset = every tool":
545
545
 
546
546
  | `CANVAS_ROLE` | Tools exposed | Typical use |
547
547
  |---------------|---------------|-------------|
548
- | _(unset)_ | all (~169) | default; backwards-compatible |
549
- | `student` | ~58 | a student's own courses, grades, submissions, and read-only course content |
548
+ | _(unset)_ | all (~172) | default; backwards-compatible |
549
+ | `student` | ~61 | a student's own courses, grades, submissions, and read-only course content |
550
550
  | `teacher` | ~149 | grading, roster, content authoring, analytics |
551
551
  | `admin` | ~161 | everything `teacher` sees plus account-level tools (`enroll_user`, `list_account_users`, …) |
552
552
 
@@ -0,0 +1,35 @@
1
+ import type { CanvasHttpClient } from './client';
2
+ import type { CanvasActivityStreamEntry, CanvasActivityStreamSummaryEntry } from './types';
3
+ export interface ActivityStreamQuery {
4
+ onlyActiveCourses?: boolean;
5
+ /**
6
+ * Stop following `Link: rel="next"` once this many items have accumulated.
7
+ * Bounds the WORK, not just the result: `paginate()` otherwise follows every
8
+ * page to exhaustion (up to `maxPaginationPages`, default 1000) before
9
+ * returning, so slicing afterwards would still have fetched up to 100,000
10
+ * items.
11
+ */
12
+ maxItems?: number;
13
+ }
14
+ export declare class ActivityStreamModule {
15
+ private client;
16
+ constructor(client: CanvasHttpClient);
17
+ /**
18
+ * `GET /users/self/activity_stream/summary` is the one stream endpoint Canvas
19
+ * does not paginate (`api_render_stream_summary` has no `Api.paginate` call),
20
+ * so this uses `request()` rather than `paginate()` — the inverse of every
21
+ * other activity-stream endpoint.
22
+ */
23
+ getSummary(onlyActiveCourses?: boolean): Promise<CanvasActivityStreamSummaryEntry[]>;
24
+ /**
25
+ * `GET /users/self/activity_stream` — the cross-course stream itself.
26
+ *
27
+ * Uses `paginate()`, which is not interchangeable with `request()` here:
28
+ * Canvas renders this endpoint through `Api.paginate(scope, self, …,
29
+ * default_per_page: 21)`, so a `request()` call would silently return 21
30
+ * items with no error and no `Link` header followed. `paginate()` sets
31
+ * `per_page=100` (Canvas's `MAX_PER_PAGE`) and follows the links.
32
+ */
33
+ getStream(query?: ActivityStreamQuery): Promise<CanvasActivityStreamEntry[]>;
34
+ }
35
+ //# sourceMappingURL=activity-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity-stream.d.ts","sourceRoot":"","sources":["../../src/canvas/activity-stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EAAE,yBAAyB,EAAE,gCAAgC,EAAE,MAAM,SAAS,CAAA;AAE1F,MAAM,WAAW,mBAAmB;IAClC,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,qBAAa,oBAAoB;IACnB,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,gBAAgB;IAE5C;;;;;OAKG;IACG,UAAU,CAAC,iBAAiB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,gCAAgC,EAAE,CAAC;IAO1F;;;;;;;;OAQG;IACG,SAAS,CAAC,KAAK,GAAE,mBAAwB,GAAG,OAAO,CAAC,yBAAyB,EAAE,CAAC;CAOvF"}
@@ -25,7 +25,9 @@ export declare class CanvasHttpClient {
25
25
  * which is the expected response for DELETE operations.
26
26
  */
27
27
  request<T>(endpoint: string, options?: CanvasRequestOptions): Promise<T>;
28
- paginate<T>(endpoint: string, params?: CanvasQueryParams): Promise<T[]>;
28
+ paginate<T>(endpoint: string, params?: CanvasQueryParams, options?: {
29
+ maxItems?: number;
30
+ }): Promise<T[]>;
29
31
  paginateEnvelope<T>(endpoint: string, envelopeKey: string, params?: CanvasQueryParams): Promise<T[]>;
30
32
  /**
31
33
  * The header block every Canvas request shares. Defining it in one place is
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/canvas/client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAEnE,OAAO,KAAK,EAAE,kBAAkB,EAAuB,MAAM,SAAS,CAAA;AAEtE,qBAAa,cAAe,SAAQ,KAAK;IACvC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;gBAEJ,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAM9D;AAKD,MAAM,WAAW,oBAAqB,SAAQ,WAAW;IACvD;;;;OAIG;IACH,KAAK,CAAC,EAAE,iBAAiB,CAAA;CAC1B;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,kBAAkB,CAAQ;gBAEtB,MAAM,EAAE,kBAAkB;IAMtC,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED;;;;OAIG;IACG,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,CAAC,CAAC;IA2C5E,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAkCvE,gBAAgB,CAAC,CAAC,EACtB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,iBAAiB,GACzB,OAAO,CAAC,CAAC,EAAE,CAAC;IAsCf;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IAQrB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,aAAa;CAKtB"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/canvas/client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAEnE,OAAO,KAAK,EAAE,kBAAkB,EAAuB,MAAM,SAAS,CAAA;AAEtE,qBAAa,cAAe,SAAQ,KAAK;IACvC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;gBAEJ,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAM9D;AAKD,MAAM,WAAW,oBAAqB,SAAQ,WAAW;IACvD;;;;OAIG;IACH,KAAK,CAAC,EAAE,iBAAiB,CAAA;CAC1B;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,kBAAkB,CAAQ;gBAEtB,MAAM,EAAE,kBAAkB;IAMtC,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED;;;;OAIG;IACG,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,CAAC,CAAC;IA2C5E,QAAQ,CAAC,CAAC,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,iBAAiB,EAC1B,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAC9B,OAAO,CAAC,CAAC,EAAE,CAAC;IA6CT,gBAAgB,CAAC,CAAC,EACtB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,iBAAiB,GACzB,OAAO,CAAC,CAAC,EAAE,CAAC;IAsCf;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IAQrB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,aAAa;CAKtB"}
@@ -6,13 +6,13 @@
6
6
 
7
7
 
8
8
 
9
- var _chunk66PSR2DZcjs = require('../chunk-66PSR2DZ.cjs');
9
+ var _chunkEMIO4XD4cjs = require('../chunk-EMIO4XD4.cjs');
10
10
 
11
11
 
12
12
 
13
13
 
14
14
 
15
- var _chunkKNYGBEL5cjs = require('../chunk-KNYGBEL5.cjs');
15
+ var _chunkC2C3TYWDcjs = require('../chunk-C2C3TYWD.cjs');
16
16
 
17
17
 
18
18
 
@@ -25,5 +25,5 @@ var _chunkKNYGBEL5cjs = require('../chunk-KNYGBEL5.cjs');
25
25
 
26
26
 
27
27
 
28
- exports.CANVAS_MAX_ID = _chunk66PSR2DZcjs.CANVAS_MAX_ID; exports.CanvasApiError = _chunkKNYGBEL5cjs.CanvasApiError; exports.CanvasClient = _chunk66PSR2DZcjs.CanvasClient; exports.CanvasHttpClient = _chunkKNYGBEL5cjs.CanvasHttpClient; exports.MAX_SAFE_CANVAS_NUMBER_ID = _chunk66PSR2DZcjs.MAX_SAFE_CANVAS_NUMBER_ID; exports.appendCanvasQuery = _chunkKNYGBEL5cjs.appendCanvasQuery; exports.canvasIdInput = _chunk66PSR2DZcjs.canvasIdInput; exports.canvasIdList = _chunk66PSR2DZcjs.canvasIdList; exports.compareCanvasIds = _chunk66PSR2DZcjs.compareCanvasIds; exports.normalizeCanvasIdInput = _chunk66PSR2DZcjs.normalizeCanvasIdInput; exports.toCanvasQuery = _chunkKNYGBEL5cjs.toCanvasQuery;
28
+ exports.CANVAS_MAX_ID = _chunkEMIO4XD4cjs.CANVAS_MAX_ID; exports.CanvasApiError = _chunkC2C3TYWDcjs.CanvasApiError; exports.CanvasClient = _chunkEMIO4XD4cjs.CanvasClient; exports.CanvasHttpClient = _chunkC2C3TYWDcjs.CanvasHttpClient; exports.MAX_SAFE_CANVAS_NUMBER_ID = _chunkEMIO4XD4cjs.MAX_SAFE_CANVAS_NUMBER_ID; exports.appendCanvasQuery = _chunkC2C3TYWDcjs.appendCanvasQuery; exports.canvasIdInput = _chunkEMIO4XD4cjs.canvasIdInput; exports.canvasIdList = _chunkEMIO4XD4cjs.canvasIdList; exports.compareCanvasIds = _chunkEMIO4XD4cjs.compareCanvasIds; exports.normalizeCanvasIdInput = _chunkEMIO4XD4cjs.normalizeCanvasIdInput; exports.toCanvasQuery = _chunkC2C3TYWDcjs.toCanvasQuery;
29
29
  //# sourceMappingURL=index.cjs.map
@@ -25,6 +25,8 @@ import { ContentMigrationsModule } from './content-migrations';
25
25
  import { GradingStandardsModule } from './grading-standards';
26
26
  import { LatePolicyModule } from './late-policy';
27
27
  import { AppointmentGroupsModule } from './appointment-groups';
28
+ import { ActivityStreamModule } from './activity-stream';
29
+ import { PlannerModule } from './planner';
28
30
  /**
29
31
  * Standalone Canvas REST API client. Composed of modular per-domain classes
30
32
  * sharing a `CanvasHttpClient`. Throws `CanvasApiError` on Canvas-side failures.
@@ -65,6 +67,8 @@ export declare class CanvasClient {
65
67
  gradingStandards: GradingStandardsModule;
66
68
  latePolicy: LatePolicyModule;
67
69
  appointmentGroups: AppointmentGroupsModule;
70
+ activityStream: ActivityStreamModule;
71
+ planner: PlannerModule;
68
72
  constructor(config: CanvasClientConfig);
69
73
  }
70
74
  export { CanvasHttpClient, CanvasApiError } from './client';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/canvas/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/canvas/index.ts"],"names":[],"mappings":"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"}
@@ -6,13 +6,13 @@ import {
6
6
  canvasIdList,
7
7
  compareCanvasIds,
8
8
  normalizeCanvasIdInput
9
- } from "../chunk-5B3JDDYC.js";
9
+ } from "../chunk-FP4A34TD.js";
10
10
  import {
11
11
  CanvasApiError,
12
12
  CanvasHttpClient,
13
13
  appendCanvasQuery,
14
14
  toCanvasQuery
15
- } from "../chunk-VVIZW7HE.js";
15
+ } from "../chunk-W5DF5YBG.js";
16
16
  export {
17
17
  CANVAS_MAX_ID,
18
18
  CanvasApiError,
@@ -0,0 +1,31 @@
1
+ import type { CanvasHttpClient } from './client';
2
+ import type { CanvasPlannerItem } from './types';
3
+ export interface PlannerItemsQuery {
4
+ startDate?: string;
5
+ endDate?: string;
6
+ contextCodes?: string[];
7
+ filter?: 'new_activity' | 'incomplete_items' | 'complete_items';
8
+ /**
9
+ * Stop following `Link: rel="next"` once this many items have accumulated.
10
+ * Same rationale as `ActivityStreamQuery.maxItems`: bounds the WORK, not
11
+ * just the result.
12
+ */
13
+ maxItems?: number;
14
+ }
15
+ export declare class PlannerModule {
16
+ private client;
17
+ constructor(client: CanvasHttpClient);
18
+ /**
19
+ * `GET /planner/items` — unions nine Canvas collections (assignments,
20
+ * ungraded quizzes, planner notes, wiki pages, ungraded discussions,
21
+ * calendar events, peer reviews, sub-assignments, peer-review
22
+ * sub-assignments) into one cross-course/cross-group feed.
23
+ *
24
+ * Uses `paginate()`: `Api.paginate(items, self, …)` sets no
25
+ * `default_per_page`, so Canvas falls back to `Api::PER_PAGE = 10` absent an
26
+ * explicit `per_page` (BRU-2797 §3.2) — the same `request()`-vs-`paginate()`
27
+ * hazard as the activity stream.
28
+ */
29
+ listItems(query?: PlannerItemsQuery): Promise<CanvasPlannerItem[]>;
30
+ }
31
+ //# sourceMappingURL=planner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planner.d.ts","sourceRoot":"","sources":["../../src/canvas/planner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAEhD,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,MAAM,CAAC,EAAE,cAAc,GAAG,kBAAkB,GAAG,gBAAgB,CAAA;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,qBAAa,aAAa;IACZ,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,gBAAgB;IAE5C;;;;;;;;;;OAUG;IACG,SAAS,CAAC,KAAK,GAAE,iBAAsB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;CAY7E"}
@@ -285,12 +285,69 @@ export interface CanvasAttachment {
285
285
  content_type: string;
286
286
  size: number;
287
287
  }
288
+ /**
289
+ * Canvas's `user_display_json` (`lib/api/v1/user.rb`): `{id, anonymous_id,
290
+ * display_name, avatar_image_url, html_url, pronouns}`, where `display_name` is
291
+ * `user.short_name` — a REAL NAME. It is returned `{}` when the user is nil, so
292
+ * every field is optional.
293
+ *
294
+ * This shape shows up wherever Canvas wants "enough to render a byline", and it
295
+ * is easy to miss because it spells the name `display_name` rather than `name`:
296
+ * a wrapper that looks for `name` / `user_name` / `author_name` walks straight
297
+ * past it. The pseudonymizer rewrites `display_name` and nulls the sibling
298
+ * `avatar_image_url` / `pronouns` — a face and a pronoun set beside a masked
299
+ * name are the same leak — but never `id` / `anonymous_id` / `html_url`, which
300
+ * are identifiers, and identifiers are preserved everywhere in this layer.
301
+ */
302
+ export interface CanvasUserDisplay {
303
+ id?: CanvasId;
304
+ anonymous_id?: string;
305
+ display_name?: string;
306
+ avatar_image_url?: string | null;
307
+ html_url?: string;
308
+ pronouns?: string | null;
309
+ [key: string]: unknown;
310
+ }
288
311
  export interface CanvasSubmissionComment {
289
312
  id: CanvasId;
290
- author_id: CanvasId;
313
+ /**
314
+ * `null` when the viewer lacks `:read_author` on the comment:
315
+ * `submission_comment_json`'s `else` branch sets `author_id: nil` together
316
+ * with `author: {}` and `author_name: "Anonymous User"`
317
+ * (lib/api/v1/submission_comment.rb:68-72 at the pinned Canvas SHA
318
+ * 1c9f0bb8013ed69c4f2efe11fd483025469b7e6c).
319
+ *
320
+ * ~~Declared non-null.~~ Widened 2026-10-10 (BRU-2865): the declaration had
321
+ * contradicted that branch since the branch was first cited, and the two
322
+ * sites that key a pseudonym off this field were written against the
323
+ * declaration rather than the serializer. Derive the key with
324
+ * `submissionCommentAuthorKey` rather than `String(author_id)` — `null`
325
+ * means "this comment carries no identity at all", not "key the map on the
326
+ * string `null`".
327
+ */
328
+ author_id: CanvasId | null;
291
329
  author_name: string;
292
330
  comment: string;
293
331
  created_at: string;
332
+ /**
333
+ * `submission_comment_json` sets this to `user_display_json(comment.author,
334
+ * …)` whenever the viewer holds `:read_author` — so a comment normally
335
+ * carries the author's `display_name` BESIDE `author_name`, and masking only
336
+ * `author_name` leaves the real name one key over.
337
+ *
338
+ * ~~Optional because the anonymous-moderated branch replaces it with `{}`.~~
339
+ * Corrected 2026-10-10 (BRU-2864): it is `submission_comment_json`'s OWN
340
+ * `else` branch that sets `author: {}`, together with `author_id: nil` and
341
+ * `author_name: "Anonymous User"`, when the viewer lacks `:read_author`
342
+ * (lib/api/v1/submission_comment.rb:68-72 at the pinned Canvas SHA
343
+ * 1c9f0bb8013ed69c4f2efe11fd483025469b7e6c).
344
+ * `anonymous_moderated_submission_comments_json` — the serializer that does
345
+ * `json.delete(:author_id)` — never emits this key at all, because
346
+ * `ANONYMOUS_MODERATED_JSON_ATTRIBUTES` (lines 24-36) has no `author` entry.
347
+ * So there is no shape in either serializer where `author.id` survives an
348
+ * absent `author_id`, and this key is never a fallback source for it.
349
+ */
350
+ author?: CanvasUserDisplay;
294
351
  }
295
352
  export interface CanvasGradebookHistoryGrader {
296
353
  id: CanvasId;
@@ -873,6 +930,274 @@ export interface CanvasActivityStreamItem {
873
930
  count: number;
874
931
  unread_count: number;
875
932
  }
933
+ /**
934
+ * One entry from GET /users/self/activity_stream/summary — `{type, count,
935
+ * unread_count}`. `CanvasActivityStreamItem` above already models this exact
936
+ * shape under a misleading name (it is used for the *course* activity stream
937
+ * summary, not a stream item); this is the correctly-named declaration for the
938
+ * cross-course summary endpoint. Renaming the existing type is tracked
939
+ * separately (BRU-2797 §1 C2, §11 Q4) and is not bundled here.
940
+ */
941
+ export interface CanvasActivityStreamSummaryEntry {
942
+ type: string;
943
+ count: number;
944
+ unread_count: number;
945
+ }
946
+ /**
947
+ * One `root_discussion_entries[]` entry on a `DiscussionTopic` / `Announcement`
948
+ * activity-stream item. Canvas caps the array at `StreamItem::LATEST_ENTRY_LIMIT`
949
+ * (3) at write time, and the nested `user` is the only place the stream spells a
950
+ * discussion author with an identifier beside the name.
951
+ *
952
+ * `user_id` can be missing or `null` — the pinned serializer SHA
953
+ * (1c9f0bb8013ed69c4f2efe11fd483025469b7e6c) can emit this nested `user`
954
+ * object with no usable identity (BRU-2868). There is nothing to key a stable
955
+ * pseudonym on in that case; see `anonymizeActivityStreamEntry`.
956
+ */
957
+ export interface CanvasActivityStreamRootEntry {
958
+ user: {
959
+ user_id?: CanvasId | null;
960
+ user_name: string;
961
+ };
962
+ message: string | null;
963
+ }
964
+ /**
965
+ * One `latest_messages[]` entry on a `Conversation` activity-stream item, built
966
+ * by `StreamItem#prepare_conversation`: `{id, created_at, author_id, message,
967
+ * participating_user_ids}`.
968
+ *
969
+ * Note what is NOT here: no name of any kind. The stream's conversation arm
970
+ * carries identifiers and message text only, which is why
971
+ * `Pseudonymizer.anonymizeActivityStream` has nothing to rewrite on it (the
972
+ * pseudonymizer rewrites names, never ids — `applyPseudonymToUser` preserves
973
+ * `user.id`). The message body is handled by provenance fencing instead.
974
+ */
975
+ export interface CanvasActivityStreamConversationMessage {
976
+ id: CanvasId;
977
+ created_at: string;
978
+ author_id: CanvasId;
979
+ message: string;
980
+ participating_user_ids: CanvasId[];
981
+ }
982
+ /**
983
+ * One `discussion_entries[]` entry on the activity stream's `Submission` arm.
984
+ *
985
+ * `submission_attempt_json` emits `discussion_entries` for any submission to a
986
+ * `discussion_topic` assignment — it is in the default-on
987
+ * `SUBMISSION_OTHER_FIELDS`, and the stream narrows nothing — by calling
988
+ * `discussion_entry_api_json(entries, topic.context, user, session)` with four
989
+ * arguments, i.e. under the default `includes = %i[user_name subentries
990
+ * display_user]`.
991
+ *
992
+ * All three identity surfaces below can carry a real name, and they do NOT
993
+ * appear together. Per `serialize_entry`, `user_name` and `user_id` are both
994
+ * omitted when the entry is deleted, while `user` is emitted whenever
995
+ * `:display_user` is in includes — that guard has no `deleted?` check. So a
996
+ * deleted entry is `{user: {id, display_name, …}}` with no `user_id` and no
997
+ * `user_name`: keying off `user_name` alone silently passes that name through.
998
+ *
999
+ * `recent_replies` is the recursion and the only subentry key upstream emits
1000
+ * (`discussion_entry_subentries`, capped at 10 with `has_more_replies`).
1001
+ * Upstream nests exactly one level — the method returns `{}` unless
1002
+ * `entry.root_entry_id.nil?` — but this type and its pseudonymizer are
1003
+ * recursive regardless, because a depth assumption is not worth a leak.
1004
+ */
1005
+ export interface CanvasSubmissionDiscussionEntry {
1006
+ id?: CanvasId;
1007
+ created_at?: string;
1008
+ updated_at?: string;
1009
+ parent_id?: CanvasId | null;
1010
+ /** Omitted when the entry is deleted. */
1011
+ user_id?: CanvasId;
1012
+ /** Omitted when the entry is deleted. */
1013
+ user_name?: string;
1014
+ /** Present even on a deleted entry — see the note above. */
1015
+ user?: CanvasUserDisplay;
1016
+ editor_id?: CanvasId;
1017
+ message?: string;
1018
+ deleted?: boolean;
1019
+ recent_replies?: CanvasSubmissionDiscussionEntry[];
1020
+ has_more_replies?: boolean;
1021
+ [key: string]: unknown;
1022
+ }
1023
+ /**
1024
+ * One entry from `GET /users/self/activity_stream`.
1025
+ *
1026
+ * `context_type` is `"Course"` | `"Group"` and EXACTLY ONE of `course_id` /
1027
+ * `group_id` is present — the key name is derived from the context type by
1028
+ * Canvas's `Api::V1::Context#context_data`, so the other key is ABSENT, not
1029
+ * null. Canvas's own doc block shows both keys with `group_id: null`; it is
1030
+ * wrong. Every optional field is therefore `?` and never `| null`: a
1031
+ * `null`-checking consumer cannot detect an absent key.
1032
+ *
1033
+ * The index signature is deliberate, not laziness. The `Submission` arm merges a
1034
+ * whole `submission_json(submission, assignment, …, includes = %w[submission_comments
1035
+ * assignment course html_url user])` into the item, so the key set of that one arm
1036
+ * is as wide as Canvas's submission serializer and grows with it. The fields named
1037
+ * below are the ones this server reads, pseudonymizes or fences; everything else
1038
+ * passes through untyped rather than being silently dropped from the declaration.
1039
+ */
1040
+ export interface CanvasActivityStreamEntry {
1041
+ /** The stream item's own id, not the underlying object's. */
1042
+ id: CanvasId;
1043
+ created_at: string;
1044
+ updated_at: string;
1045
+ title: string | null;
1046
+ message: string | null;
1047
+ /**
1048
+ * A Ruby class name (`stream_item.data.class.name`), overwritten to
1049
+ * `"WebConference"` / `"Collaboration"` for those arms. Not a closed set —
1050
+ * `(string & {})` keeps it open, because the value is computed at runtime and
1051
+ * the serializer's `else` arm raises rather than enumerating.
1052
+ */
1053
+ type: 'DiscussionTopic' | 'Announcement' | 'Conversation' | 'Message' | 'Submission' | 'WebConference' | 'Collaboration' | 'AssessmentRequest' | 'DiscussionEntry' | (string & {});
1054
+ /** Re-derived from `conversation_participants` on the `Conversation` arm, where it may be null. */
1055
+ read_state: boolean | null;
1056
+ context_type?: 'Course' | 'Group' | (string & {});
1057
+ course_id?: CanvasId;
1058
+ group_id?: CanvasId;
1059
+ html_url?: string;
1060
+ /** `Message` items set `url` alongside `html_url`, both from `data.url`. */
1061
+ url?: string;
1062
+ discussion_topic_id?: CanvasId;
1063
+ announcement_id?: CanvasId;
1064
+ total_root_discussion_entries?: number;
1065
+ require_initial_post?: boolean | null;
1066
+ user_has_posted?: boolean | null;
1067
+ root_discussion_entries?: CanvasActivityStreamRootEntry[];
1068
+ conversation_id?: CanvasId;
1069
+ private?: boolean;
1070
+ participant_count?: number;
1071
+ latest_messages?: CanvasActivityStreamConversationMessage[];
1072
+ message_id?: CanvasId;
1073
+ notification_category?: string | null;
1074
+ submission_id?: CanvasId;
1075
+ assignment_id?: CanvasId;
1076
+ user_id?: CanvasId;
1077
+ grader_id?: CanvasId | null;
1078
+ body?: string | null;
1079
+ user?: CanvasUser;
1080
+ submission_comments?: CanvasSubmissionComment[];
1081
+ /**
1082
+ * Discussion-topic submissions only, but default-on when they occur — see
1083
+ * {@link CanvasSubmissionDiscussionEntry}. Name-bearing and recursive.
1084
+ */
1085
+ discussion_entries?: CanvasSubmissionDiscussionEntry[];
1086
+ /**
1087
+ * `attempt.proxy_submitter.short_name` — a BARE STRING, not a user object,
1088
+ * emitted with its `proxy_submitter_id` sibling whenever
1089
+ * `attempt.proxy_submission?`. Because it is a naked string there is no
1090
+ * `user`-shaped container for a generic walker to find.
1091
+ */
1092
+ proxy_submitter?: string;
1093
+ proxy_submitter_id?: CanvasId;
1094
+ web_conference_id?: CanvasId;
1095
+ collaboration_id?: CanvasId;
1096
+ assessment_request_id?: CanvasId;
1097
+ /**
1098
+ * `DiscussionEntry` only. The serializer emits this name with NO accompanying
1099
+ * identifier (`hash["author_name"] = discussion_entry.author_name` is the whole
1100
+ * arm beside `message`), so a stable pseudonym is not derivable for it — see
1101
+ * `Pseudonymizer.anonymizeActivityStream`.
1102
+ */
1103
+ author_name?: string;
1104
+ [key: string]: unknown;
1105
+ }
1106
+ /**
1107
+ * `PlannerOverride` json from `planner_override_json` — present only when the
1108
+ * student has toggled an item's planner visibility ("mark as done" /
1109
+ * "dismiss"). It is `api_json` on the AR record plus a re-derived
1110
+ * `plannable_type` and `assignment_id`. None of its fields identify anyone
1111
+ * but the authenticated caller — it is their own planner override.
1112
+ */
1113
+ export interface CanvasPlannerOverride {
1114
+ id: CanvasId;
1115
+ plannable_id: CanvasId;
1116
+ plannable_type: string;
1117
+ user_id: CanvasId;
1118
+ assignment_id: CanvasId | null;
1119
+ marked_complete: boolean;
1120
+ dismissed: boolean;
1121
+ created_at: string;
1122
+ updated_at: string;
1123
+ deleted_at: string | null;
1124
+ [key: string]: unknown;
1125
+ }
1126
+ /**
1127
+ * The caller's own submission status on a planner item's underlying
1128
+ * Assignment / SubAssignment / PeerReviewSubAssignment, from
1129
+ * `submission_statuses_for` → `submission_statuses`.
1130
+ */
1131
+ export interface CanvasPlannerSubmissionStatus {
1132
+ submitted?: boolean;
1133
+ excused?: boolean;
1134
+ graded?: boolean;
1135
+ posted_at?: string | null;
1136
+ late?: boolean;
1137
+ missing?: boolean;
1138
+ needs_grading?: boolean;
1139
+ has_feedback?: boolean;
1140
+ redo_request?: boolean;
1141
+ feedback?: {
1142
+ comment: string;
1143
+ is_media: boolean;
1144
+ author_name?: string;
1145
+ author_avatar_url?: string | null;
1146
+ };
1147
+ [key: string]: unknown;
1148
+ }
1149
+ /**
1150
+ * One entry from `GET /planner/items`.
1151
+ *
1152
+ * `context_type` / `course_id` / `group_id` share the exact derivation and
1153
+ * absent-not-null caveat as {@link CanvasActivityStreamEntry}
1154
+ * (`Api::V1::Context#context_data`, BRU-2797 §1 C7) — a group-context item
1155
+ * has no `course_id` key at all, not a null one.
1156
+ *
1157
+ * `plannable` is the polymorphic underlying object; its shape is keyed by
1158
+ * `plannable_type` and varies per arm (`lib/api/v1/planner_item.rb`
1159
+ * `planner_item_json`, pinned SHA 1c9f0bb8013ed69c4f2efe11fd483025469b7e6c).
1160
+ * No arm carries a third-party user record — the richest is `planner_note`,
1161
+ * whose own `user_id` is the authenticated caller's own id, never another
1162
+ * student's (BRU-2797 §6.1) — so this is declared as a loose object rather
1163
+ * than a per-arm union.
1164
+ */
1165
+ export interface CanvasPlannerItem {
1166
+ context_type?: 'Course' | 'Group' | (string & {});
1167
+ course_id?: CanvasId;
1168
+ group_id?: CanvasId;
1169
+ /** The caller's own course nickname when set, else the course name — join on `course_id`, never this. */
1170
+ context_name?: string;
1171
+ context_image?: string | null;
1172
+ plannable_id: CanvasId;
1173
+ /**
1174
+ * A `PlannerHelper::PLANNABLE_TYPES` key — snake_case, NOT the Ruby class
1175
+ * name, unlike the activity stream's `type`. Not a closed set — kept open
1176
+ * with `(string & {})` for the same reason as the activity stream's `type`.
1177
+ */
1178
+ plannable_type: 'assignment' | 'quiz' | 'planner_note' | 'wiki_page' | 'discussion_topic' | 'announcement' | 'calendar_event' | 'assessment_request' | 'sub_assignment' | 'peer_review_sub_assignment' | (string & {});
1179
+ /** `null` unless the student has toggled this item's planner visibility. */
1180
+ planner_override: CanvasPlannerOverride | null;
1181
+ /**
1182
+ * `false` or an object — a union, not a nullable object. Canvas initialises
1183
+ * `submission_status = { submissions: false }` and only replaces it with a
1184
+ * hash for a submittable plannable; a consumer writing
1185
+ * `item.submissions?.graded` would silently read `undefined` on the
1186
+ * boolean arm if this were declared `CanvasPlannerSubmissionStatus | null`.
1187
+ */
1188
+ submissions: false | CanvasPlannerSubmissionStatus;
1189
+ new_activity: boolean;
1190
+ plannable_date: string;
1191
+ /** The polymorphic underlying object. Shape varies by `plannable_type`; no arm carries a third-party user record. */
1192
+ plannable: Record<string, unknown>;
1193
+ /** Absent for `planner_note` items — Canvas has no html_url for individual planner notes. */
1194
+ html_url?: string;
1195
+ /** `sub_assignment` discussion-checkpoint items only. */
1196
+ details?: {
1197
+ reply_to_entry_required_count: number;
1198
+ };
1199
+ [key: string]: unknown;
1200
+ }
876
1201
  export interface CanvasStudentSummary {
877
1202
  id: CanvasId;
878
1203
  page_views: number;