canvas-lms-mcp 1.24.0 → 1.25.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 (83) hide show
  1. package/README.md +22 -2
  2. package/dist/canvas/concurrency.d.ts +19 -0
  3. package/dist/canvas/concurrency.d.ts.map +1 -0
  4. package/dist/canvas/index.cjs +3 -3
  5. package/dist/canvas/index.js +2 -2
  6. package/dist/canvas/pages.d.ts +11 -3
  7. package/dist/canvas/pages.d.ts.map +1 -1
  8. package/dist/{chunk-LKR72KFD.js → chunk-AWWVBRRP.js} +2 -2
  9. package/dist/chunk-AWWVBRRP.js.map +1 -0
  10. package/dist/{chunk-AI4XQHKN.cjs → chunk-C7JF7KW3.cjs} +256 -61
  11. package/dist/chunk-C7JF7KW3.cjs.map +1 -0
  12. package/dist/{chunk-5ZWXPFMW.js → chunk-DSK2KKLD.js} +226 -31
  13. package/dist/chunk-DSK2KKLD.js.map +1 -0
  14. package/dist/{chunk-6II3HREL.js → chunk-NKPSDUV4.js} +30 -6
  15. package/dist/chunk-NKPSDUV4.js.map +1 -0
  16. package/dist/{chunk-4GMSSKK6.cjs → chunk-PUI5V45I.cjs} +33 -9
  17. package/dist/chunk-PUI5V45I.cjs.map +1 -0
  18. package/dist/{chunk-7VJLBXZD.cjs → chunk-VCUQ5RA6.cjs} +2 -2
  19. package/dist/chunk-VCUQ5RA6.cjs.map +1 -0
  20. package/dist/discovery/manifests.d.ts +1 -0
  21. package/dist/discovery/manifests.d.ts.map +1 -1
  22. package/dist/http.cjs +5 -5
  23. package/dist/http.js +3 -3
  24. package/dist/init.cjs +3 -3
  25. package/dist/init.js +1 -1
  26. package/dist/server.cjs +4 -4
  27. package/dist/server.js +3 -3
  28. package/dist/stdio.cjs +4 -4
  29. package/dist/stdio.js +3 -3
  30. package/dist/tools/accessibility-audit.d.ts.map +1 -1
  31. package/dist/tools/accounts.d.ts.map +1 -1
  32. package/dist/tools/analytics.d.ts.map +1 -1
  33. package/dist/tools/appointment-groups.d.ts.map +1 -1
  34. package/dist/tools/assignment-overrides.d.ts.map +1 -1
  35. package/dist/tools/assignments.d.ts.map +1 -1
  36. package/dist/tools/attention.d.ts.map +1 -1
  37. package/dist/tools/calendar.d.ts.map +1 -1
  38. package/dist/tools/content-exports.d.ts.map +1 -1
  39. package/dist/tools/content-migrations.d.ts.map +1 -1
  40. package/dist/tools/conversations.d.ts.map +1 -1
  41. package/dist/tools/course-setup.d.ts.map +1 -1
  42. package/dist/tools/courses.d.ts.map +1 -1
  43. package/dist/tools/dashboard.d.ts.map +1 -1
  44. package/dist/tools/discussions.d.ts.map +1 -1
  45. package/dist/tools/enrollments.d.ts.map +1 -1
  46. package/dist/tools/files.d.ts.map +1 -1
  47. package/dist/tools/grade-explanation.d.ts.map +1 -1
  48. package/dist/tools/grade-projection.d.ts.map +1 -1
  49. package/dist/tools/gradebook-history.d.ts.map +1 -1
  50. package/dist/tools/grading-policy.d.ts.map +1 -1
  51. package/dist/tools/grading-standards.d.ts.map +1 -1
  52. package/dist/tools/groups.d.ts.map +1 -1
  53. package/dist/tools/health.d.ts.map +1 -1
  54. package/dist/tools/index.d.ts.map +1 -1
  55. package/dist/tools/link-audit.d.ts.map +1 -1
  56. package/dist/tools/modules.d.ts.map +1 -1
  57. package/dist/tools/new-quiz-accommodations.d.ts.map +1 -1
  58. package/dist/tools/new-quizzes.d.ts.map +1 -1
  59. package/dist/tools/outcomes.d.ts.map +1 -1
  60. package/dist/tools/pages.d.ts.map +1 -1
  61. package/dist/tools/peer-reviews.d.ts.map +1 -1
  62. package/dist/tools/pseudonym.d.ts.map +1 -1
  63. package/dist/tools/quiz-accommodations.d.ts.map +1 -1
  64. package/dist/tools/quiz-question-responses.d.ts.map +1 -1
  65. package/dist/tools/quizzes.d.ts.map +1 -1
  66. package/dist/tools/rubrics.d.ts.map +1 -1
  67. package/dist/tools/student-search.d.ts.map +1 -1
  68. package/dist/tools/student.d.ts.map +1 -1
  69. package/dist/tools/submission-files.d.ts.map +1 -1
  70. package/dist/tools/submissions-awaiting-grading.d.ts.map +1 -1
  71. package/dist/tools/submissions.d.ts.map +1 -1
  72. package/dist/tools/titles.d.ts +26 -0
  73. package/dist/tools/titles.d.ts.map +1 -0
  74. package/dist/tools/types.d.ts +9 -0
  75. package/dist/tools/types.d.ts.map +1 -1
  76. package/dist/tools/users.d.ts.map +1 -1
  77. package/package.json +1 -13
  78. package/dist/chunk-4GMSSKK6.cjs.map +0 -1
  79. package/dist/chunk-5ZWXPFMW.js.map +0 -1
  80. package/dist/chunk-6II3HREL.js.map +0 -1
  81. package/dist/chunk-7VJLBXZD.cjs.map +0 -1
  82. package/dist/chunk-AI4XQHKN.cjs.map +0 -1
  83. package/dist/chunk-LKR72KFD.js.map +0 -1
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
8
  [![Node](https://img.shields.io/badge/node-%3E%3D22-brightgreen)](https://nodejs.org)
9
9
  [![npm downloads](https://img.shields.io/npm/dw/canvas-lms-mcp)](https://www.npmjs.com/package/canvas-lms-mcp)
10
- [![MCP Registry](https://img.shields.io/badge/MCP%20Registry-io.github.bruchris%2Fcanvas--lms--mcp-blue)](https://registry.modelcontextprotocol.io/v0/servers/io.github.bruchris%2Fcanvas-lms-mcp)
10
+ [![MCP Registry](https://img.shields.io/badge/MCP%20Registry-io.github.bruchris%2Fcanvas--lms--mcp-blue)](https://registry.modelcontextprotocol.io/v0/servers/io.github.bruchris%2Fcanvas-lms-mcp/versions/latest)
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
 
@@ -17,12 +17,20 @@ MCP server for [Canvas LMS](https://www.instructure.com/canvas). Read courses, a
17
17
 
18
18
  1. **[Download `canvas-lms-mcp.mcpb`](https://github.com/bruchris/canvas-lms-mcp/releases/latest/download/canvas-lms-mcp.mcpb)** from the latest release.
19
19
  2. Double-click the file (or drag it into Claude Desktop's Extensions settings).
20
- 3. When prompted, paste your Canvas API token and Canvas API base URL (e.g. `https://school.instructure.com/api/v1`). Teachers and staff handling student data can also flip **FERPA mode — pseudonymize students** on in the same dialog ([what it does](#ferpa-mode-student-pseudonymization)).
20
+ 3. When prompted, paste your Canvas API token and Canvas base URL — your institution's origin only, e.g. `https://school.instructure.com` (do not append `/api/v1`). Teachers and staff handling student data can also flip **FERPA mode — pseudonymize students** on in the same dialog ([what it does](#ferpa-mode-student-pseudonymization)).
21
21
 
22
22
  No terminal, no Node.js install, no config-file editing — Claude Desktop bundles the runtime and handles config for you. The same `.mcpb` works in Claude Code and MCP for Windows.
23
23
 
24
24
  Prefer the terminal? Use the [Quick Start](#quick-start) below.
25
25
 
26
+ ## One-click install (Cursor / VS Code)
27
+
28
+ [![Add to Cursor](https://img.shields.io/badge/Add%20to-Cursor-black?logo=cursor&logoColor=white)](cursor://anysphere.cursor-deeplink/mcp/install?name=canvas-lms-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImNhbnZhcy1sbXMtbWNwIl0sImVudiI6eyJDQU5WQVNfQVBJX1RPS0VOIjoieW91ci10b2tlbi1oZXJlIiwiQ0FOVkFTX0JBU0VfVVJMIjoiaHR0cHM6Ly95b3VyLWluc3RpdHV0aW9uLmluc3RydWN0dXJlLmNvbSJ9fQ==)
29
+ [![Install in VS Code](https://img.shields.io/badge/Install%20in-VS%20Code-007ACC?logo=visual-studio-code&logoColor=white)](vscode:mcp/install?%7B%22name%22%3A%22canvas-lms%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22canvas-lms-mcp%22%5D%2C%22env%22%3A%7B%22CANVAS_API_TOKEN%22%3A%22your-token-here%22%2C%22CANVAS_BASE_URL%22%3A%22https%3A%2F%2Fyour-institution.instructure.com%22%7D%7D)
30
+ [![Install in VS Code Insiders](https://img.shields.io/badge/Install%20in-VS%20Code%20Insiders-24bfa5?logo=visual-studio-code&logoColor=white)](vscode-insiders:mcp/install?%7B%22name%22%3A%22canvas-lms%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22canvas-lms-mcp%22%5D%2C%22env%22%3A%7B%22CANVAS_API_TOKEN%22%3A%22your-token-here%22%2C%22CANVAS_BASE_URL%22%3A%22https%3A%2F%2Fyour-institution.instructure.com%22%7D%7D)
31
+
32
+ Click a badge to open Cursor or VS Code with canvas-lms-mcp pre-configured (placeholder credentials filled in — replace with your actual Canvas API token and base URL after install). For manual config-file setup, see [docs/manual-setup.md](./docs/manual-setup.md).
33
+
26
34
  ## Comparison
27
35
 
28
36
  | | canvas-lms-mcp | [vishalsachdev/canvas-mcp](https://github.com/vishalsachdev/canvas-mcp) | [DMontgomery40/mcp-canvas-lms](https://github.com/DMontgomery40/mcp-canvas-lms) |
@@ -67,6 +75,8 @@ npx skills add bruchris/canvas-lms-mcp
67
75
  | `canvas-at-risk-students` | Surface students with missing assignments or declining grades and send targeted outreach |
68
76
  | `canvas-gradebook-audit` | Inspect the full grade-change audit trail — who changed what grade, when, and by how much |
69
77
  | `canvas-outcome-tracker` | Track learning outcome mastery and class-wide proficiency for accreditation and program review |
78
+ | `canvas-accessibility-sweep` | Pre-launch WCAG accessibility and broken-link sweep of a course, with a prioritised remediation list |
79
+ | `canvas-office-hours` | Create, publish, and manage Canvas Scheduler office-hour sign-up slots and see who reserved |
70
80
 
71
81
  Skills are markdown workflow files (no extra dependencies). They work with the MCP server you already have installed. See the [`skills/` directory](./skills/) for the full list.
72
82
 
@@ -365,6 +375,16 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md) for the full contribution and validatio
365
375
  - [Educator Assignment Review Workflow](docs/workflows/educator-assignment-review.md) -- Read-first grading flow with write-safety guidance
366
376
  - [Student Weekly Planning Workflow](docs/workflows/student-weekly-planning.md) -- Read-only weekly planning sequence for students
367
377
 
378
+ ## Privacy Policy
379
+
380
+ `canvas-lms-mcp` runs entirely on your own machine. The maintainers operate no
381
+ servers and collect no telemetry or analytics — your Canvas API token and all
382
+ Canvas data stay local and travel only between your machine and your own Canvas
383
+ instance. Optional FERPA pseudonymization runs locally; the only data written to
384
+ disk is the optional pseudonym map and audit log. Full details — data
385
+ collection, usage, storage, third-party sharing, retention, and contact — are in
386
+ [PRIVACY.md](PRIVACY.md).
387
+
368
388
  ## License
369
389
 
370
390
  MIT
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Run `fn` across `items` with at most `limit` invocations in flight at once,
3
+ * returning the results in the SAME order as `items` regardless of the order in
4
+ * which the individual calls settle (`results[i]` always corresponds to
5
+ * `items[i]`).
6
+ *
7
+ * This is the single shared implementation of the batched fan-out first written
8
+ * inline for `find_student_across_courses` (PR #252). Whole-course scans such as
9
+ * {@link PagesModule.listWithBodies} would otherwise issue one Canvas request per
10
+ * item all at once — a 300-page course means 300 simultaneous requests with no
11
+ * backstop. Bounding the fan-out keeps a large course from hammering Canvas.
12
+ *
13
+ * Error semantics match a plain `Promise.all`: the first rejection propagates
14
+ * unchanged and no further batches are started. Call sites that want to tolerate
15
+ * per-item failures (like `find_student_across_courses`) do so by catching inside
16
+ * `fn` and resolving with a discriminated result, never by letting `fn` throw.
17
+ */
18
+ export declare function mapWithConcurrency<T, R>(items: readonly T[], limit: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
19
+ //# sourceMappingURL=concurrency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"concurrency.d.ts","sourceRoot":"","sources":["../../src/canvas/concurrency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAC3C,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GACzC,OAAO,CAAC,CAAC,EAAE,CAAC,CAcd"}
@@ -1,17 +1,17 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunk4GMSSKK6cjs = require('../chunk-4GMSSKK6.cjs');
3
+ var _chunkPUI5V45Icjs = require('../chunk-PUI5V45I.cjs');
4
4
 
5
5
 
6
6
 
7
7
 
8
8
 
9
- var _chunk7VJLBXZDcjs = require('../chunk-7VJLBXZD.cjs');
9
+ var _chunkVCUQ5RA6cjs = require('../chunk-VCUQ5RA6.cjs');
10
10
 
11
11
 
12
12
 
13
13
 
14
14
 
15
15
 
16
- exports.CanvasApiError = _chunk7VJLBXZDcjs.CanvasApiError; exports.CanvasClient = _chunk4GMSSKK6cjs.CanvasClient; exports.CanvasHttpClient = _chunk7VJLBXZDcjs.CanvasHttpClient; exports.appendCanvasQuery = _chunk7VJLBXZDcjs.appendCanvasQuery; exports.toCanvasQuery = _chunk7VJLBXZDcjs.toCanvasQuery;
16
+ exports.CanvasApiError = _chunkVCUQ5RA6cjs.CanvasApiError; exports.CanvasClient = _chunkPUI5V45Icjs.CanvasClient; exports.CanvasHttpClient = _chunkVCUQ5RA6cjs.CanvasHttpClient; exports.appendCanvasQuery = _chunkVCUQ5RA6cjs.appendCanvasQuery; exports.toCanvasQuery = _chunkVCUQ5RA6cjs.toCanvasQuery;
17
17
  //# sourceMappingURL=index.cjs.map
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  CanvasClient
3
- } from "../chunk-6II3HREL.js";
3
+ } from "../chunk-NKPSDUV4.js";
4
4
  import {
5
5
  CanvasApiError,
6
6
  CanvasHttpClient,
7
7
  appendCanvasQuery,
8
8
  toCanvasQuery
9
- } from "../chunk-LKR72KFD.js";
9
+ } from "../chunk-AWWVBRRP.js";
10
10
  export {
11
11
  CanvasApiError,
12
12
  CanvasClient,
@@ -1,5 +1,11 @@
1
1
  import type { CanvasHttpClient } from './client';
2
2
  import type { CanvasPage } from './types';
3
+ /**
4
+ * Max concurrent page-body fetches issued by {@link PagesModule.listWithBodies}.
5
+ * Matches the `CONCURRENT_COURSE_LIMIT` precedent in `student-search.ts`; kept
6
+ * as a named export so the cap is greppable and adjustable in one place.
7
+ */
8
+ export declare const PAGE_BODY_CONCURRENCY_LIMIT = 10;
3
9
  export declare class PagesModule {
4
10
  private client;
5
11
  constructor(client: CanvasHttpClient);
@@ -8,9 +14,11 @@ export declare class PagesModule {
8
14
  /**
9
15
  * List every page in a course with its full HTML `body`. The paginated list
10
16
  * endpoint returns page stubs without `body`, so this fans out a `get()` per
11
- * page (mirroring the `listWithItems` pattern in `modules.ts`). Used by the
12
- * link-audit tool to scan page content. `CanvasApiError` from either the list
13
- * or any individual fetch propagates unchanged.
17
+ * page, bounded to at most {@link PAGE_BODY_CONCURRENCY_LIMIT} concurrent
18
+ * fetches so a large course does not open one Canvas request per page all at
19
+ * once. Result order matches the listed page order. Used by the link-audit and
20
+ * accessibility-audit tools to scan page content. `CanvasApiError` from either
21
+ * the list or any individual fetch propagates unchanged.
14
22
  */
15
23
  listWithBodies(courseId: number): Promise<CanvasPage[]>;
16
24
  create(courseId: number, params: {
@@ -1 +1 @@
1
- {"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../../src/canvas/pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC,qBAAa,WAAW;IACV,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,gBAAgB;IAEtC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAI7C,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAMjE;;;;;;OAMG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAKvD,MAAM,CACV,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GACpF,OAAO,CAAC,UAAU,CAAC;IAOhB,MAAM,CACV,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GACrF,OAAO,CAAC,UAAU,CAAC;IAUhB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAM/D"}
1
+ {"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../../src/canvas/pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGzC;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,KAAK,CAAA;AAE7C,qBAAa,WAAW;IACV,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,gBAAgB;IAEtC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAI7C,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAMjE;;;;;;;;OAQG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAOvD,MAAM,CACV,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GACpF,OAAO,CAAC,UAAU,CAAC;IAOhB,MAAM,CACV,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GACrF,OAAO,CAAC,UAAU,CAAC;IAUhB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAM/D"}
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "1.24.0";
2
+ var version = "1.25.0";
3
3
 
4
4
  // src/canvas/query.ts
5
5
  function appendCanvasQuery(target, params) {
@@ -163,4 +163,4 @@ export {
163
163
  CanvasApiError,
164
164
  CanvasHttpClient
165
165
  };
166
- //# sourceMappingURL=chunk-LKR72KFD.js.map
166
+ //# sourceMappingURL=chunk-AWWVBRRP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../package.json","../src/canvas/query.ts","../src/canvas/client.ts"],"sourcesContent":["{\n \"name\": \"canvas-lms-mcp\",\n \"version\": \"1.25.0\",\n \"mcpName\": \"io.github.bruchris/canvas-lms-mcp\",\n \"description\": \"TypeScript MCP 1.x server for Canvas LMS — 163 tools across Canvas courses, assignments, grades, gradebook history, quizzes, New Quizzes (LTI), outcomes, discussions, admin workflows, and more.\",\n \"type\": \"module\",\n \"types\": \"./dist/server.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/server.d.ts\",\n \"import\": \"./dist/server.js\",\n \"require\": \"./dist/server.cjs\"\n },\n \"./stdio\": {\n \"types\": \"./dist/stdio.d.ts\",\n \"import\": \"./dist/stdio.js\",\n \"require\": \"./dist/stdio.cjs\"\n },\n \"./http\": {\n \"types\": \"./dist/http.d.ts\",\n \"import\": \"./dist/http.js\",\n \"require\": \"./dist/http.cjs\"\n },\n \"./canvas\": {\n \"types\": \"./dist/canvas/index.d.ts\",\n \"import\": \"./dist/canvas/index.js\",\n \"require\": \"./dist/canvas/index.cjs\"\n }\n },\n \"bin\": {\n \"canvas-lms-mcp\": \"bin/canvas-lms-mcp.js\"\n },\n \"files\": [\n \"bin/\",\n \"dist/\"\n ],\n \"scripts\": {\n \"dev\": \"tsup --watch\",\n \"build\": \"tsup && tsc -p tsconfig.build.json\",\n \"generate:manifests\": \"tsx scripts/generate-manifests.ts\",\n \"mcpb:pack\": \"node scripts/pack-mcpb.mjs\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"lint\": \"eslint src/ tests/ && prettier --check src/ tests/\",\n \"lint:fix\": \"eslint src/ tests/ --fix && prettier --write src/ tests/\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"keywords\": [\n \"canvas\",\n \"canvas-lms\",\n \"lms\",\n \"mcp\",\n \"model-context-protocol\",\n \"education\",\n \"grading\",\n \"ai-agent\",\n \"ai-tools\",\n \"claude-desktop\",\n \"cursor\",\n \"vscode\",\n \"instructure\",\n \"education-technology\",\n \"edtech\",\n \"teacher\",\n \"student\",\n \"grading-automation\",\n \"typescript\"\n ],\n \"author\": \"Christian Bru\",\n \"license\": \"MIT\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/bruchris/canvas-lms-mcp.git\"\n },\n \"engines\": {\n \"node\": \">=22\"\n },\n \"packageManager\": \"pnpm@10.11.0\",\n \"dependencies\": {\n \"@iarna/toml\": \"^2.2.5\",\n \"@modelcontextprotocol/sdk\": \"^1.30.0\",\n \"prompts\": \"^2.4.2\",\n \"zod\": \"^4.4.3\"\n },\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@types/node\": \"^25.9.3\",\n \"@types/prompts\": \"^2.4.9\",\n \"@typescript-eslint/eslint-plugin\": \"^8.65.0\",\n \"@typescript-eslint/parser\": \"^8.65.0\",\n \"@vitest/coverage-v8\": \"^4.1.10\",\n \"eslint\": \"^10.7.0\",\n \"eslint-config-prettier\": \"^10.1.8\",\n \"eslint-plugin-prettier\": \"^5.5.6\",\n \"prettier\": \"^3.9.6\",\n \"tsup\": \"^8.5.1\",\n \"tsx\": \"^4.23.1\",\n \"typescript\": \"^6.0.3\",\n \"typescript-eslint\": \"^8.65.0\",\n \"vite\": \"^8.1.5\",\n \"vitest\": \"^4.1.10\"\n }\n}\n","export type CanvasQueryPrimitive = string | number | boolean\n\nexport type CanvasQueryValue = CanvasQueryPrimitive | ReadonlyArray<CanvasQueryPrimitive>\n\nexport type CanvasQueryParams = Record<string, CanvasQueryValue | undefined | null>\n\nexport function appendCanvasQuery(target: URLSearchParams, params?: CanvasQueryParams): void {\n if (!params) return\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined || value === null) continue\n if (Array.isArray(value)) {\n if (value.length === 0) continue\n const arrayKey = key.endsWith('[]') ? key : `${key}[]`\n for (const item of value) {\n if (item === undefined || item === null) continue\n target.append(arrayKey, String(item))\n }\n } else {\n target.set(key, String(value))\n }\n }\n}\n\nexport function toCanvasQuery(params?: CanvasQueryParams): URLSearchParams {\n const search = new URLSearchParams()\n appendCanvasQuery(search, params)\n return search\n}\n","import { version } from '../../package.json'\nimport { appendCanvasQuery, type CanvasQueryParams } from './query'\nimport type { CanvasClientConfig, CanvasErrorResponse } from './types'\n\nexport class CanvasApiError extends Error {\n status: number\n endpoint: string\n\n constructor(message: string, status: number, endpoint: string) {\n super(message)\n this.name = 'CanvasApiError'\n this.status = status\n this.endpoint = endpoint\n }\n}\n\nconst DEFAULT_MAX_PAGINATION_PAGES = 1000\nconst USER_AGENT = `canvas-lms-mcp/${version}`\n\nexport interface CanvasRequestOptions extends RequestInit {\n /**\n * Query params to append to the endpoint URL. Arrays produce repeated\n * `key[]=v1&key[]=v2` entries (the convention Canvas uses for `include[]`).\n * Preserves any query string already present on the endpoint.\n */\n query?: CanvasQueryParams\n}\n\nexport class CanvasHttpClient {\n private token: string\n private _baseUrl: string\n private maxPaginationPages: number\n\n constructor(config: CanvasClientConfig) {\n this.token = config.token\n this._baseUrl = config.baseUrl.replace(/\\/+$/, '')\n this.maxPaginationPages = config.maxPaginationPages ?? DEFAULT_MAX_PAGINATION_PAGES\n }\n\n get baseUrl(): string {\n return this._baseUrl\n }\n\n /**\n * Makes a single authenticated request to the Canvas API.\n * Returns `undefined` (typed as `T`) when Canvas responds with 204 No Content,\n * which is the expected response for DELETE operations.\n */\n async request<T>(endpoint: string, options: CanvasRequestOptions = {}): Promise<T> {\n const { query, ...init } = options\n let url = endpoint.startsWith('http') ? endpoint : `${this._baseUrl}${endpoint}`\n if (query) {\n const parsed = new URL(url)\n appendCanvasQuery(parsed.searchParams, query)\n url = parsed.toString()\n }\n\n const method = (init.method ?? 'GET').toUpperCase()\n if (init.body != null && (method === 'GET' || method === 'HEAD')) {\n throw new Error(\n `GET requests must not include a body (Canvas CloudFront CDN rejects them with 403): ${endpoint}`,\n )\n }\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.token}`,\n 'User-Agent': USER_AGENT,\n }\n if (init.body) {\n headers['Content-Type'] = 'application/json'\n }\n\n const response = await fetch(url, {\n ...init,\n headers: {\n ...headers,\n ...init.headers,\n },\n })\n\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as CanvasErrorResponse\n const message =\n body.errors?.[0]?.message ?? body.message ?? `Canvas API error: ${response.status}`\n throw new CanvasApiError(message, response.status, endpoint)\n }\n\n if (response.status === 204) {\n return undefined as T\n }\n\n return response.json() as Promise<T>\n }\n\n async paginate<T>(endpoint: string, params?: CanvasQueryParams): Promise<T[]> {\n const url = new URL(`${this._baseUrl}${endpoint}`)\n if (!url.searchParams.has('per_page')) {\n url.searchParams.set('per_page', '100')\n }\n appendCanvasQuery(url.searchParams, params)\n\n const results: T[] = []\n let nextUrl: string | null = url.toString()\n let pages = 0\n\n while (nextUrl && pages < this.maxPaginationPages) {\n const response = await fetch(nextUrl, {\n headers: {\n Authorization: `Bearer ${this.token}`,\n 'User-Agent': USER_AGENT,\n },\n })\n\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as CanvasErrorResponse\n const message =\n body.errors?.[0]?.message ?? body.message ?? `Canvas API error: ${response.status}`\n throw new CanvasApiError(message, response.status, endpoint)\n }\n\n const data = (await response.json()) as T[]\n results.push(...data)\n pages++\n\n nextUrl = this.parseNextLink(response.headers.get('Link'))\n }\n\n return results\n }\n\n async paginateEnvelope<T>(\n endpoint: string,\n envelopeKey: string,\n params?: CanvasQueryParams,\n ): Promise<T[]> {\n const url = new URL(`${this._baseUrl}${endpoint}`)\n if (!url.searchParams.has('per_page')) {\n url.searchParams.set('per_page', '100')\n }\n appendCanvasQuery(url.searchParams, params)\n\n const results: T[] = []\n let nextUrl: string | null = url.toString()\n let pages = 0\n\n while (nextUrl && pages < this.maxPaginationPages) {\n const response = await fetch(nextUrl, {\n headers: {\n Authorization: `Bearer ${this.token}`,\n 'User-Agent': USER_AGENT,\n },\n })\n\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as CanvasErrorResponse\n const message =\n body.errors?.[0]?.message ?? body.message ?? `Canvas API error: ${response.status}`\n throw new CanvasApiError(message, response.status, endpoint)\n }\n\n const body = (await response.json()) as Record<string, T[]>\n const data = body[envelopeKey] ?? []\n results.push(...data)\n pages++\n\n nextUrl = this.parseNextLink(response.headers.get('Link'))\n }\n\n return results\n }\n\n private parseNextLink(linkHeader: string | null): string | null {\n if (!linkHeader) return null\n const match = linkHeader.match(/<([^>]+)>;\\s*rel=\"next\"/)\n return match?.[1] ?? null\n }\n}\n"],"mappings":"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","names":["body"]}