canvas-lms-mcp 1.32.0 → 1.32.1

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.
@@ -2,7 +2,30 @@
2
2
 
3
3
  const sub = process.argv[2]
4
4
 
5
- if (sub === 'init') {
5
+ // Handled before any import so help works without Canvas credentials and never
6
+ // starts the stdio or HTTP transport.
7
+ const USAGE = `Usage: canvas-lms-mcp [command] [options]
8
+
9
+ Canvas LMS MCP server. With no command, starts the stdio transport.
10
+
11
+ Commands:
12
+ (none) Start the stdio MCP server (Claude Desktop, Cursor, VS Code)
13
+ serve Start the HTTP MCP server (--port <n>, default 3001; --host <addr>)
14
+ init Configure MCP clients (run \`canvas-lms-mcp init --help\` for options)
15
+ doctor Check setup without starting the server
16
+ auth status Same as doctor
17
+
18
+ Credentials (required by stdio and serve, not by help or init --help):
19
+ --token <t> Canvas API token (or CANVAS_API_TOKEN)
20
+ --base-url <u> Canvas base URL, origin only (or CANVAS_BASE_URL)
21
+
22
+ Options:
23
+ -h, --help Show this help and exit
24
+ `
25
+
26
+ if (sub === '--help' || sub === '-h') {
27
+ console.log(USAGE)
28
+ } else if (sub === 'init') {
6
29
  await import('../dist/init.js')
7
30
  } else if (sub === 'serve') {
8
31
  await import('../dist/http.js')
@@ -1,17 +1,17 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkDELZA7F6cjs = require('../chunk-DELZA7F6.cjs');
3
+ var _chunkJXM4QLYVcjs = require('../chunk-JXM4QLYV.cjs');
4
4
 
5
5
 
6
6
 
7
7
 
8
8
 
9
- var _chunk26UC2T3Rcjs = require('../chunk-26UC2T3R.cjs');
9
+ var _chunkUXJUBCTVcjs = require('../chunk-UXJUBCTV.cjs');
10
10
 
11
11
 
12
12
 
13
13
 
14
14
 
15
15
 
16
- exports.CanvasApiError = _chunk26UC2T3Rcjs.CanvasApiError; exports.CanvasClient = _chunkDELZA7F6cjs.CanvasClient; exports.CanvasHttpClient = _chunk26UC2T3Rcjs.CanvasHttpClient; exports.appendCanvasQuery = _chunk26UC2T3Rcjs.appendCanvasQuery; exports.toCanvasQuery = _chunk26UC2T3Rcjs.toCanvasQuery;
16
+ exports.CanvasApiError = _chunkUXJUBCTVcjs.CanvasApiError; exports.CanvasClient = _chunkJXM4QLYVcjs.CanvasClient; exports.CanvasHttpClient = _chunkUXJUBCTVcjs.CanvasHttpClient; exports.appendCanvasQuery = _chunkUXJUBCTVcjs.appendCanvasQuery; exports.toCanvasQuery = _chunkUXJUBCTVcjs.toCanvasQuery;
17
17
  //# sourceMappingURL=index.cjs.map
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  CanvasClient
3
- } from "../chunk-H3LDGHYB.js";
3
+ } from "../chunk-UXGS7LAH.js";
4
4
  import {
5
5
  CanvasApiError,
6
6
  CanvasHttpClient,
7
7
  appendCanvasQuery,
8
8
  toCanvasQuery
9
- } from "../chunk-LYNXEFQN.js";
9
+ } from "../chunk-6FRP3JSU.js";
10
10
  export {
11
11
  CanvasApiError,
12
12
  CanvasClient,
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "1.32.0";
2
+ var version = "1.32.1";
3
3
 
4
4
  // src/canvas/query.ts
5
5
  function appendCanvasQuery(target, params) {
@@ -176,4 +176,4 @@ export {
176
176
  CanvasApiError,
177
177
  CanvasHttpClient
178
178
  };
179
- //# sourceMappingURL=chunk-LYNXEFQN.js.map
179
+ //# sourceMappingURL=chunk-6FRP3JSU.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../package.json","../src/canvas/query.ts","../src/canvas/client.ts"],"sourcesContent":["{\n \"name\": \"canvas-lms-mcp\",\n \"version\": \"1.32.0\",\n \"mcpName\": \"io.github.bruchris/canvas-lms-mcp\",\n \"description\": \"TypeScript MCP 1.x server for Canvas LMS — 167 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 \"skills/\"\n ],\n \"scripts\": {\n \"dev\": \"tsup --watch\",\n \"build\": \"tsup && tsc -p tsconfig.build.json\",\n \"generate:manifests\": \"tsx scripts/generate-manifests.ts\",\n \"generate:prompts\": \"tsx scripts/generate-prompts.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.32.0\",\n \"prompts\": \"^2.4.2\",\n \"zod\": \"^4.4.3\"\n },\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@types/jsdom\": \"^30.0.0\",\n \"@types/node\": \"^26.4.0\",\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 \"ajv\": \"^8.20.0\",\n \"eslint\": \"^10.7.0\",\n \"eslint-config-prettier\": \"^10.1.8\",\n \"eslint-plugin-prettier\": \"^5.5.6\",\n \"jsdom\": \"^30.0.1\",\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 \"yaml\": \"^2.8.3\"\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 this.assertNotTruncated(nextUrl, endpoint)\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 this.assertNotTruncated(nextUrl, endpoint)\n return results\n }\n\n /**\n * The pagination loops exit with a non-null `nextUrl` only when the page cap\n * was reached while Canvas still advertised another page. Surface that as an\n * error rather than returning a partial list that looks complete.\n */\n private assertNotTruncated(nextUrl: string | null, endpoint: string): void {\n if (!nextUrl) return\n throw new Error(\n `Results are incomplete: ${endpoint} has more pages beyond the configured limit of ${this.maxPaginationPages} pages, so the list was truncated. Narrow the query with filters or raise maxPaginationPages.`,\n )\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":";AAEE,cAAW;;;ACIN,SAAS,kBAAkB,QAAyB,QAAkC;AAC3F,MAAI,CAAC,OAAQ;AACb,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,QAAI,UAAU,UAAa,UAAU,KAAM;AAC3C,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,UAAI,MAAM,WAAW,EAAG;AACxB,YAAM,WAAW,IAAI,SAAS,IAAI,IAAI,MAAM,GAAG,GAAG;AAClD,iBAAW,QAAQ,OAAO;AACxB,YAAI,SAAS,UAAa,SAAS,KAAM;AACzC,eAAO,OAAO,UAAU,OAAO,IAAI,CAAC;AAAA,MACtC;AAAA,IACF,OAAO;AACL,aAAO,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,IAC/B;AAAA,EACF;AACF;AAEO,SAAS,cAAc,QAA6C;AACzE,QAAM,SAAS,IAAI,gBAAgB;AACnC,oBAAkB,QAAQ,MAAM;AAChC,SAAO;AACT;;;ACvBO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC;AAAA,EACA;AAAA,EAEA,YAAY,SAAiB,QAAgB,UAAkB;AAC7D,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,WAAW;AAAA,EAClB;AACF;AAEA,IAAM,+BAA+B;AACrC,IAAM,aAAa,kBAAkB,OAAO;AAWrC,IAAM,mBAAN,MAAuB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,QAA4B;AACtC,SAAK,QAAQ,OAAO;AACpB,SAAK,WAAW,OAAO,QAAQ,QAAQ,QAAQ,EAAE;AACjD,SAAK,qBAAqB,OAAO,sBAAsB;AAAA,EACzD;AAAA,EAEA,IAAI,UAAkB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAW,UAAkB,UAAgC,CAAC,GAAe;AACjF,UAAM,EAAE,OAAO,GAAG,KAAK,IAAI;AAC3B,QAAI,MAAM,SAAS,WAAW,MAAM,IAAI,WAAW,GAAG,KAAK,QAAQ,GAAG,QAAQ;AAC9E,QAAI,OAAO;AACT,YAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,wBAAkB,OAAO,cAAc,KAAK;AAC5C,YAAM,OAAO,SAAS;AAAA,IACxB;AAEA,UAAM,UAAU,KAAK,UAAU,OAAO,YAAY;AAClD,QAAI,KAAK,QAAQ,SAAS,WAAW,SAAS,WAAW,SAAS;AAChE,YAAM,IAAI;AAAA,QACR,uFAAuF,QAAQ;AAAA,MACjG;AAAA,IACF;AAEA,UAAM,UAAkC;AAAA,MACtC,eAAe,UAAU,KAAK,KAAK;AAAA,MACnC,cAAc;AAAA,IAChB;AACA,QAAI,KAAK,MAAM;AACb,cAAQ,cAAc,IAAI;AAAA,IAC5B;AAEA,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,GAAG;AAAA,MACH,SAAS;AAAA,QACP,GAAG;AAAA,QACH,GAAG,KAAK;AAAA,MACV;AAAA,IACF,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAQ,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACpD,YAAM,UACJ,KAAK,SAAS,CAAC,GAAG,WAAW,KAAK,WAAW,qBAAqB,SAAS,MAAM;AACnF,YAAM,IAAI,eAAe,SAAS,SAAS,QAAQ,QAAQ;AAAA,IAC7D;AAEA,QAAI,SAAS,WAAW,KAAK;AAC3B,aAAO;AAAA,IACT;AAEA,WAAO,SAAS,KAAK;AAAA,EACvB;AAAA,EAEA,MAAM,SAAY,UAAkB,QAA0C;AAC5E,UAAM,MAAM,IAAI,IAAI,GAAG,KAAK,QAAQ,GAAG,QAAQ,EAAE;AACjD,QAAI,CAAC,IAAI,aAAa,IAAI,UAAU,GAAG;AACrC,UAAI,aAAa,IAAI,YAAY,KAAK;AAAA,IACxC;AACA,sBAAkB,IAAI,cAAc,MAAM;AAE1C,UAAM,UAAe,CAAC;AACtB,QAAI,UAAyB,IAAI,SAAS;AAC1C,QAAI,QAAQ;AAEZ,WAAO,WAAW,QAAQ,KAAK,oBAAoB;AACjD,YAAM,WAAW,MAAM,MAAM,SAAS;AAAA,QACpC,SAAS;AAAA,UACP,eAAe,UAAU,KAAK,KAAK;AAAA,UACnC,cAAc;AAAA,QAChB;AAAA,MACF,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,OAAQ,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACpD,cAAM,UACJ,KAAK,SAAS,CAAC,GAAG,WAAW,KAAK,WAAW,qBAAqB,SAAS,MAAM;AACnF,cAAM,IAAI,eAAe,SAAS,SAAS,QAAQ,QAAQ;AAAA,MAC7D;AAEA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,cAAQ,KAAK,GAAG,IAAI;AACpB;AAEA,gBAAU,KAAK,cAAc,SAAS,QAAQ,IAAI,MAAM,CAAC;AAAA,IAC3D;AAEA,SAAK,mBAAmB,SAAS,QAAQ;AACzC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,iBACJ,UACA,aACA,QACc;AACd,UAAM,MAAM,IAAI,IAAI,GAAG,KAAK,QAAQ,GAAG,QAAQ,EAAE;AACjD,QAAI,CAAC,IAAI,aAAa,IAAI,UAAU,GAAG;AACrC,UAAI,aAAa,IAAI,YAAY,KAAK;AAAA,IACxC;AACA,sBAAkB,IAAI,cAAc,MAAM;AAE1C,UAAM,UAAe,CAAC;AACtB,QAAI,UAAyB,IAAI,SAAS;AAC1C,QAAI,QAAQ;AAEZ,WAAO,WAAW,QAAQ,KAAK,oBAAoB;AACjD,YAAM,WAAW,MAAM,MAAM,SAAS;AAAA,QACpC,SAAS;AAAA,UACP,eAAe,UAAU,KAAK,KAAK;AAAA,UACnC,cAAc;AAAA,QAChB;AAAA,MACF,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAMA,QAAQ,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACpD,cAAM,UACJA,MAAK,SAAS,CAAC,GAAG,WAAWA,MAAK,WAAW,qBAAqB,SAAS,MAAM;AACnF,cAAM,IAAI,eAAe,SAAS,SAAS,QAAQ,QAAQ;AAAA,MAC7D;AAEA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,YAAM,OAAO,KAAK,WAAW,KAAK,CAAC;AACnC,cAAQ,KAAK,GAAG,IAAI;AACpB;AAEA,gBAAU,KAAK,cAAc,SAAS,QAAQ,IAAI,MAAM,CAAC;AAAA,IAC3D;AAEA,SAAK,mBAAmB,SAAS,QAAQ;AACzC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,mBAAmB,SAAwB,UAAwB;AACzE,QAAI,CAAC,QAAS;AACd,UAAM,IAAI;AAAA,MACR,2BAA2B,QAAQ,kDAAkD,KAAK,kBAAkB;AAAA,IAC9G;AAAA,EACF;AAAA,EAEQ,cAAc,YAA0C;AAC9D,QAAI,CAAC,WAAY,QAAO;AACxB,UAAM,QAAQ,WAAW,MAAM,yBAAyB;AACxD,WAAO,QAAQ,CAAC,KAAK;AAAA,EACvB;AACF;","names":["body"]}
1
+ {"version":3,"sources":["../package.json","../src/canvas/query.ts","../src/canvas/client.ts"],"sourcesContent":["{\n \"name\": \"canvas-lms-mcp\",\n \"version\": \"1.32.1\",\n \"mcpName\": \"io.github.bruchris/canvas-lms-mcp\",\n \"description\": \"TypeScript MCP 1.x server for Canvas LMS — 167 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 \"skills/\"\n ],\n \"scripts\": {\n \"dev\": \"tsup --watch\",\n \"build\": \"tsup && tsc -p tsconfig.build.json\",\n \"generate:manifests\": \"tsx scripts/generate-manifests.ts\",\n \"generate:prompts\": \"tsx scripts/generate-prompts.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.32.0\",\n \"prompts\": \"^2.4.2\",\n \"zod\": \"^4.4.3\"\n },\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@types/jsdom\": \"^30.0.0\",\n \"@types/node\": \"^26.4.0\",\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 \"ajv\": \"^8.20.0\",\n \"eslint\": \"^10.7.0\",\n \"eslint-config-prettier\": \"^10.1.8\",\n \"eslint-plugin-prettier\": \"^5.5.6\",\n \"jsdom\": \"^30.0.1\",\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 \"yaml\": \"^2.8.3\"\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 this.assertNotTruncated(nextUrl, endpoint)\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 this.assertNotTruncated(nextUrl, endpoint)\n return results\n }\n\n /**\n * The pagination loops exit with a non-null `nextUrl` only when the page cap\n * was reached while Canvas still advertised another page. Surface that as an\n * error rather than returning a partial list that looks complete.\n */\n private assertNotTruncated(nextUrl: string | null, endpoint: string): void {\n if (!nextUrl) return\n throw new Error(\n `Results are incomplete: ${endpoint} has more pages beyond the configured limit of ${this.maxPaginationPages} pages, so the list was truncated. Narrow the query with filters or raise maxPaginationPages.`,\n )\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"]}
@@ -21,11 +21,11 @@ import {
21
21
  OUTCOME_SORT_ORDER,
22
22
  SEARCH_CONTENT_TYPES,
23
23
  mapWithConcurrency
24
- } from "./chunk-H3LDGHYB.js";
24
+ } from "./chunk-UXGS7LAH.js";
25
25
  import {
26
26
  CanvasApiError,
27
27
  version
28
- } from "./chunk-LYNXEFQN.js";
28
+ } from "./chunk-6FRP3JSU.js";
29
29
 
30
30
  // src/server.ts
31
31
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -9549,4 +9549,4 @@ export {
9549
9549
  createSharedPseudonymizer,
9550
9550
  createCanvasMCPServer
9551
9551
  };
9552
- //# sourceMappingURL=chunk-5WTLGWRA.js.map
9552
+ //# sourceMappingURL=chunk-B44OQYTW.js.map
@@ -21,11 +21,11 @@ var _chunkQCEGMTXWcjs = require('./chunk-QCEGMTXW.cjs');
21
21
 
22
22
 
23
23
 
24
- var _chunkDELZA7F6cjs = require('./chunk-DELZA7F6.cjs');
24
+ var _chunkJXM4QLYVcjs = require('./chunk-JXM4QLYV.cjs');
25
25
 
26
26
 
27
27
 
28
- var _chunk26UC2T3Rcjs = require('./chunk-26UC2T3R.cjs');
28
+ var _chunkUXJUBCTVcjs = require('./chunk-UXJUBCTV.cjs');
29
29
 
30
30
  // src/server.ts
31
31
  var _mcpjs = require('@modelcontextprotocol/sdk/server/mcp.js');
@@ -989,7 +989,7 @@ function assignmentSubmissionTools(canvas) {
989
989
 
990
990
  // src/tools/errors.ts
991
991
  function formatError(error) {
992
- if (error instanceof _chunk26UC2T3Rcjs.CanvasApiError) {
992
+ if (error instanceof _chunkUXJUBCTVcjs.CanvasApiError) {
993
993
  const status = error.status;
994
994
  const message = error.message;
995
995
  const ltiMarkers = ["Tool not configured", "tool is not configured"];
@@ -1043,7 +1043,7 @@ function analyticsTools(canvas) {
1043
1043
  inputSchema: {
1044
1044
  course_id: _zod.z.number().describe("The Canvas course ID"),
1045
1045
  search_term: _zod.z.string().describe("The keyword or phrase to search for"),
1046
- content_types: _zod.z.array(_zod.z.enum(_chunkDELZA7F6cjs.SEARCH_CONTENT_TYPES)).optional().describe(
1046
+ content_types: _zod.z.array(_zod.z.enum(_chunkJXM4QLYVcjs.SEARCH_CONTENT_TYPES)).optional().describe(
1047
1047
  "Content types to search. Defaults to all types: pages, discussions, assignments, announcements."
1048
1048
  )
1049
1049
  },
@@ -1055,7 +1055,7 @@ function analyticsTools(canvas) {
1055
1055
  const courseId = params.course_id;
1056
1056
  const searchTerm = params.search_term;
1057
1057
  const types = _nullishCoalesce(params.content_types, () => ( [
1058
- ..._chunkDELZA7F6cjs.SEARCH_CONTENT_TYPES
1058
+ ..._chunkJXM4QLYVcjs.SEARCH_CONTENT_TYPES
1059
1059
  ]));
1060
1060
  if (types.length === 0) return { results: [] };
1061
1061
  const settled = await Promise.allSettled(
@@ -1308,7 +1308,7 @@ function attentionTools(canvas, pseudonymizer) {
1308
1308
  summaryMap.set(s.id, s);
1309
1309
  }
1310
1310
  } catch (err) {
1311
- if (err instanceof _chunk26UC2T3Rcjs.CanvasApiError && err.status === 404) {
1311
+ if (err instanceof _chunkUXJUBCTVcjs.CanvasApiError && err.status === 404) {
1312
1312
  analyticsAvailable = false;
1313
1313
  } else {
1314
1314
  throw err;
@@ -1662,7 +1662,7 @@ async function fanOut(config) {
1662
1662
  skipped.push(outcome.result);
1663
1663
  }
1664
1664
  } catch (err) {
1665
- if (!(err instanceof _chunk26UC2T3Rcjs.CanvasApiError)) {
1665
+ if (!(err instanceof _chunkUXJUBCTVcjs.CanvasApiError)) {
1666
1666
  console.error(`Unexpected error ${config.errorContext(item)}:`, err);
1667
1667
  }
1668
1668
  const message = err instanceof Error ? err.message : "Unknown error";
@@ -2654,7 +2654,7 @@ function discussionTools(canvas) {
2654
2654
  try {
2655
2655
  return await canvas.discussions.get(course_id, topic_id);
2656
2656
  } catch (error) {
2657
- if (!(error instanceof _chunk26UC2T3Rcjs.CanvasApiError) || error.status !== 404) throw error;
2657
+ if (!(error instanceof _chunkUXJUBCTVcjs.CanvasApiError) || error.status !== 404) throw error;
2658
2658
  const description = await describeAnonymityFromList(canvas, course_id, topic_id);
2659
2659
  if (description === void 0) throw error;
2660
2660
  throw new Error(anonymousTopicMessage(course_id, topic_id, description), {
@@ -3997,9 +3997,9 @@ function gradingPolicyTools(canvas) {
3997
3997
  latePolicySource = "api";
3998
3998
  } else {
3999
3999
  const err = latePolicyResult.reason;
4000
- if (err instanceof _chunk26UC2T3Rcjs.CanvasApiError && err.status === 403) {
4000
+ if (err instanceof _chunkUXJUBCTVcjs.CanvasApiError && err.status === 403) {
4001
4001
  policyUnavailable = true;
4002
- } else if (err instanceof _chunk26UC2T3Rcjs.CanvasApiError && err.status === 404) {
4002
+ } else if (err instanceof _chunkUXJUBCTVcjs.CanvasApiError && err.status === 404) {
4003
4003
  latePolicySource = "default";
4004
4004
  } else {
4005
4005
  throw err;
@@ -4029,7 +4029,7 @@ function gradingPolicyTools(canvas) {
4029
4029
  }
4030
4030
  }
4031
4031
  } catch (err) {
4032
- if (!(err instanceof _chunk26UC2T3Rcjs.CanvasApiError)) throw err;
4032
+ if (!(err instanceof _chunkUXJUBCTVcjs.CanvasApiError)) throw err;
4033
4033
  if (err.status !== 403 && err.status !== 404) throw err;
4034
4034
  }
4035
4035
  if (standardTitle === null) {
@@ -4159,7 +4159,7 @@ function gradingStandardsTools(canvas) {
4159
4159
  }
4160
4160
  throw new Error("Provide either course_id or account_id.");
4161
4161
  } catch (error) {
4162
- if (error instanceof _chunk26UC2T3Rcjs.CanvasApiError && error.status === 403 && courseId === void 0 && accountId !== void 0 && !error.message.toLowerCase().includes("rate limit exceeded")) {
4162
+ if (error instanceof _chunkUXJUBCTVcjs.CanvasApiError && error.status === 403 && courseId === void 0 && accountId !== void 0 && !error.message.toLowerCase().includes("rate limit exceeded")) {
4163
4163
  throw new Error(
4164
4164
  "Creating grading standards at the account level requires Canvas admin permissions. Try creating the standard in a course context instead (use course_id).",
4165
4165
  { cause: error }
@@ -4519,7 +4519,7 @@ function outcomeTools(canvas, pseudonymizer) {
4519
4519
  title: "Get Root Outcome Group",
4520
4520
  description: "Get the root outcome group for an account or course context.",
4521
4521
  inputSchema: {
4522
- context_type: _zod.z.enum(_chunkDELZA7F6cjs.OUTCOME_CONTEXT_TYPES).describe("Whether to read outcomes from an account or course context."),
4522
+ context_type: _zod.z.enum(_chunkJXM4QLYVcjs.OUTCOME_CONTEXT_TYPES).describe("Whether to read outcomes from an account or course context."),
4523
4523
  context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context.")
4524
4524
  },
4525
4525
  annotations: { readOnlyHint: true, openWorldHint: true },
@@ -4533,7 +4533,7 @@ function outcomeTools(canvas, pseudonymizer) {
4533
4533
  title: "List Outcome Groups",
4534
4534
  description: "List all outcome groups for an account or course context.",
4535
4535
  inputSchema: {
4536
- context_type: _zod.z.enum(_chunkDELZA7F6cjs.OUTCOME_CONTEXT_TYPES).describe("Whether to read outcome groups from an account or course context."),
4536
+ context_type: _zod.z.enum(_chunkJXM4QLYVcjs.OUTCOME_CONTEXT_TYPES).describe("Whether to read outcome groups from an account or course context."),
4537
4537
  context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context.")
4538
4538
  },
4539
4539
  annotations: { readOnlyHint: true, openWorldHint: true },
@@ -4547,10 +4547,10 @@ function outcomeTools(canvas, pseudonymizer) {
4547
4547
  title: "List Outcome Group Links",
4548
4548
  description: "List all outcome links in an account or course context.",
4549
4549
  inputSchema: {
4550
- context_type: _zod.z.enum(_chunkDELZA7F6cjs.OUTCOME_CONTEXT_TYPES).describe("Whether to read outcome links from an account or course context."),
4550
+ context_type: _zod.z.enum(_chunkJXM4QLYVcjs.OUTCOME_CONTEXT_TYPES).describe("Whether to read outcome links from an account or course context."),
4551
4551
  context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
4552
- outcome_style: _zod.z.enum(_chunkDELZA7F6cjs.OUTCOME_DETAIL_LEVELS).optional().describe('Outcome detail level. Use "full" to include expanded outcome fields.'),
4553
- outcome_group_style: _zod.z.enum(_chunkDELZA7F6cjs.OUTCOME_DETAIL_LEVELS).optional().describe(
4552
+ outcome_style: _zod.z.enum(_chunkJXM4QLYVcjs.OUTCOME_DETAIL_LEVELS).optional().describe('Outcome detail level. Use "full" to include expanded outcome fields.'),
4553
+ outcome_group_style: _zod.z.enum(_chunkJXM4QLYVcjs.OUTCOME_DETAIL_LEVELS).optional().describe(
4554
4554
  'Outcome group detail level. Use "full" to include expanded outcome group fields.'
4555
4555
  )
4556
4556
  },
@@ -4569,7 +4569,7 @@ function outcomeTools(canvas, pseudonymizer) {
4569
4569
  title: "Get Outcome Group",
4570
4570
  description: "Get details for a specific outcome group in an account or course context.",
4571
4571
  inputSchema: {
4572
- context_type: _zod.z.enum(_chunkDELZA7F6cjs.OUTCOME_CONTEXT_TYPES).describe("The outcome group context type."),
4572
+ context_type: _zod.z.enum(_chunkJXM4QLYVcjs.OUTCOME_CONTEXT_TYPES).describe("The outcome group context type."),
4573
4573
  context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
4574
4574
  outcome_group_id: _zod.z.number().describe("The Canvas outcome group ID.")
4575
4575
  },
@@ -4585,10 +4585,10 @@ function outcomeTools(canvas, pseudonymizer) {
4585
4585
  title: "List Outcome Group Outcomes",
4586
4586
  description: "List the linked outcomes directly under a specific outcome group.",
4587
4587
  inputSchema: {
4588
- context_type: _zod.z.enum(_chunkDELZA7F6cjs.OUTCOME_CONTEXT_TYPES).describe("The outcome group context type."),
4588
+ context_type: _zod.z.enum(_chunkJXM4QLYVcjs.OUTCOME_CONTEXT_TYPES).describe("The outcome group context type."),
4589
4589
  context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
4590
4590
  outcome_group_id: _zod.z.number().describe("The Canvas outcome group ID."),
4591
- outcome_style: _zod.z.enum(_chunkDELZA7F6cjs.OUTCOME_DETAIL_LEVELS).optional().describe('Outcome detail level. Use "full" to include expanded outcome fields.')
4591
+ outcome_style: _zod.z.enum(_chunkJXM4QLYVcjs.OUTCOME_DETAIL_LEVELS).optional().describe('Outcome detail level. Use "full" to include expanded outcome fields.')
4592
4592
  },
4593
4593
  annotations: { readOnlyHint: true, openWorldHint: true },
4594
4594
  handler: async (params) => canvas.outcomes.listGroupOutcomes(
@@ -4605,7 +4605,7 @@ function outcomeTools(canvas, pseudonymizer) {
4605
4605
  title: "List Outcome Group Subgroups",
4606
4606
  description: "List the immediate child outcome groups under a specific outcome group.",
4607
4607
  inputSchema: {
4608
- context_type: _zod.z.enum(_chunkDELZA7F6cjs.OUTCOME_CONTEXT_TYPES).describe("The outcome group context type."),
4608
+ context_type: _zod.z.enum(_chunkJXM4QLYVcjs.OUTCOME_CONTEXT_TYPES).describe("The outcome group context type."),
4609
4609
  context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
4610
4610
  outcome_group_id: _zod.z.number().describe("The Canvas outcome group ID.")
4611
4611
  },
@@ -4675,14 +4675,14 @@ function outcomeTools(canvas, pseudonymizer) {
4675
4675
  inputSchema: {
4676
4676
  course_id: _zod.z.number().describe("The Canvas course ID."),
4677
4677
  aggregate: _zod.z.enum(["course"]).optional().describe("Aggregate all student rollups into a single course-level rollup."),
4678
- aggregate_stat: _zod.z.enum(_chunkDELZA7F6cjs.OUTCOME_ROLLUP_AGGREGATE_STATS).optional().describe('Statistic to use when aggregate="course".'),
4678
+ aggregate_stat: _zod.z.enum(_chunkJXM4QLYVcjs.OUTCOME_ROLLUP_AGGREGATE_STATS).optional().describe('Statistic to use when aggregate="course".'),
4679
4679
  user_ids: _zod.z.array(_zod.z.union([_zod.z.number(), _zod.z.string()])).optional().describe('Optional Canvas user IDs or SIS user IDs prefixed with "sis_user_id:".'),
4680
4680
  outcome_ids: _zod.z.array(_zod.z.number()).optional().describe("Optional outcome IDs to restrict the rollups."),
4681
4681
  include_courses: _zod.z.boolean().optional().describe("Include linked course details in the response payload."),
4682
- exclude: _zod.z.array(_zod.z.enum(_chunkDELZA7F6cjs.OUTCOME_EXCLUDE_OPTIONS)).optional().describe("Optional rollup exclusions for missing users or missing outcome results."),
4683
- sort_by: _zod.z.enum(_chunkDELZA7F6cjs.OUTCOME_ROLLUP_SORT_BY).optional().describe("Sort rollups by student name or by a specific outcome score."),
4682
+ exclude: _zod.z.array(_zod.z.enum(_chunkJXM4QLYVcjs.OUTCOME_EXCLUDE_OPTIONS)).optional().describe("Optional rollup exclusions for missing users or missing outcome results."),
4683
+ sort_by: _zod.z.enum(_chunkJXM4QLYVcjs.OUTCOME_ROLLUP_SORT_BY).optional().describe("Sort rollups by student name or by a specific outcome score."),
4684
4684
  sort_outcome_id: _zod.z.number().optional().describe('Outcome ID to sort by when sort_by="outcome".'),
4685
- sort_order: _zod.z.enum(_chunkDELZA7F6cjs.OUTCOME_SORT_ORDER).optional().describe("Sort order to apply when sorting rollups."),
4685
+ sort_order: _zod.z.enum(_chunkJXM4QLYVcjs.OUTCOME_SORT_ORDER).optional().describe("Sort order to apply when sorting rollups."),
4686
4686
  add_defaults: _zod.z.boolean().optional().describe("Include default mastery colors and levels when Canvas supports it.")
4687
4687
  },
4688
4688
  annotations: { readOnlyHint: true, openWorldHint: true },
@@ -4732,7 +4732,7 @@ function outcomeTools(canvas, pseudonymizer) {
4732
4732
  description: "Get mastery distribution analytics for outcomes in a course, optionally filtered by students or outcomes.",
4733
4733
  inputSchema: {
4734
4734
  course_id: _zod.z.number().describe("The Canvas course ID."),
4735
- exclude: _zod.z.array(_zod.z.enum(_chunkDELZA7F6cjs.OUTCOME_EXCLUDE_OPTIONS)).optional().describe("Optional exclusions for missing users or missing outcome results."),
4735
+ exclude: _zod.z.array(_zod.z.enum(_chunkJXM4QLYVcjs.OUTCOME_EXCLUDE_OPTIONS)).optional().describe("Optional exclusions for missing users or missing outcome results."),
4736
4736
  outcome_ids: _zod.z.array(_zod.z.number()).optional().describe("Optional outcome IDs to restrict the distribution results."),
4737
4737
  student_ids: _zod.z.array(_zod.z.union([_zod.z.number(), _zod.z.string()])).optional().describe('Optional Canvas student IDs or SIS user IDs prefixed with "sis_user_id:".'),
4738
4738
  include_alignment_distributions: _zod.z.boolean().optional().describe("Include contributing score distributions for alignments."),
@@ -5582,7 +5582,7 @@ function newQuizAccommodationTools(canvas) {
5582
5582
  extra_attempts: record.extra_attempts
5583
5583
  });
5584
5584
  } catch (err) {
5585
- const message = err instanceof _chunk26UC2T3Rcjs.CanvasApiError ? err.message : "Unknown error";
5585
+ const message = err instanceof _chunkUXJUBCTVcjs.CanvasApiError ? err.message : "Unknown error";
5586
5586
  failed.push({
5587
5587
  assignment_id: assignmentId,
5588
5588
  time_multiplier: null,
@@ -6122,8 +6122,8 @@ function studentTools(canvas, pseudonymizer) {
6122
6122
  } else {
6123
6123
  coursesFailed.push({
6124
6124
  course_id: result.courseId,
6125
- status: result.err instanceof _chunk26UC2T3Rcjs.CanvasApiError ? result.err.status : null,
6126
- message: result.err instanceof _chunk26UC2T3Rcjs.CanvasApiError ? result.err.message : String(result.err)
6125
+ status: result.err instanceof _chunkUXJUBCTVcjs.CanvasApiError ? result.err.status : null,
6126
+ message: result.err instanceof _chunkUXJUBCTVcjs.CanvasApiError ? result.err.message : String(result.err)
6127
6127
  });
6128
6128
  }
6129
6129
  }
@@ -6263,7 +6263,7 @@ function studentSearchTools(canvas, pseudonymizer) {
6263
6263
  const coursesToScan = truncated ? sorted.slice(0, maxCourses) : sorted;
6264
6264
  const coursesFailed = [];
6265
6265
  const perCourseMatches = [];
6266
- const scanResults = await _chunkDELZA7F6cjs.mapWithConcurrency.call(void 0,
6266
+ const scanResults = await _chunkJXM4QLYVcjs.mapWithConcurrency.call(void 0,
6267
6267
  coursesToScan,
6268
6268
  CONCURRENT_COURSE_LIMIT,
6269
6269
  async (course) => {
@@ -6286,8 +6286,8 @@ function studentSearchTools(canvas, pseudonymizer) {
6286
6286
  } else {
6287
6287
  coursesFailed.push({
6288
6288
  course_id: r.course.id,
6289
- status: r.err instanceof _chunk26UC2T3Rcjs.CanvasApiError ? r.err.status : null,
6290
- message: r.err instanceof _chunk26UC2T3Rcjs.CanvasApiError ? r.err.message : String(r.err)
6289
+ status: r.err instanceof _chunkUXJUBCTVcjs.CanvasApiError ? r.err.status : null,
6290
+ message: r.err instanceof _chunkUXJUBCTVcjs.CanvasApiError ? r.err.message : String(r.err)
6291
6291
  });
6292
6292
  }
6293
6293
  }
@@ -6965,7 +6965,7 @@ async function scanQuizzes(canvas, courseId, warnings) {
6965
6965
  canvas.assignments.list(courseId)
6966
6966
  ]);
6967
6967
  const classicQuizzes = quizzes.filter((quiz) => CLASSIC_QUIZ_TYPES3.has(quiz.quiz_type));
6968
- const classicFindings = await _chunkDELZA7F6cjs.mapWithConcurrency.call(void 0,
6968
+ const classicFindings = await _chunkJXM4QLYVcjs.mapWithConcurrency.call(void 0,
6969
6969
  classicQuizzes,
6970
6970
  QUIZ_SCAN_CONCURRENCY_LIMIT,
6971
6971
  async (quiz) => {
@@ -6991,7 +6991,7 @@ async function scanQuizzes(canvas, courseId, warnings) {
6991
6991
  }
6992
6992
  );
6993
6993
  const newQuizAssignments = assignments.filter((a) => a.is_quiz_lti_assignment === true);
6994
- const newQuizFindings = await _chunkDELZA7F6cjs.mapWithConcurrency.call(void 0,
6994
+ const newQuizFindings = await _chunkJXM4QLYVcjs.mapWithConcurrency.call(void 0,
6995
6995
  newQuizAssignments,
6996
6996
  QUIZ_SCAN_CONCURRENCY_LIMIT,
6997
6997
  async (assignment) => {
@@ -7341,7 +7341,7 @@ async function scanQuizzesAccessibility(canvas, courseId, warnings) {
7341
7341
  canvas.assignments.list(courseId)
7342
7342
  ]);
7343
7343
  const classicQuizzes = quizzes.filter((quiz) => CLASSIC_QUIZ_TYPES4.has(quiz.quiz_type));
7344
- const classicFindings = await _chunkDELZA7F6cjs.mapWithConcurrency.call(void 0,
7344
+ const classicFindings = await _chunkJXM4QLYVcjs.mapWithConcurrency.call(void 0,
7345
7345
  classicQuizzes,
7346
7346
  QUIZ_SCAN_CONCURRENCY_LIMIT2,
7347
7347
  async (quiz) => {
@@ -7366,7 +7366,7 @@ async function scanQuizzesAccessibility(canvas, courseId, warnings) {
7366
7366
  }
7367
7367
  );
7368
7368
  const newQuizAssignments = assignments.filter((a) => a.is_quiz_lti_assignment === true);
7369
- const newQuizFindings = await _chunkDELZA7F6cjs.mapWithConcurrency.call(void 0,
7369
+ const newQuizFindings = await _chunkJXM4QLYVcjs.mapWithConcurrency.call(void 0,
7370
7370
  newQuizAssignments,
7371
7371
  QUIZ_SCAN_CONCURRENCY_LIMIT2,
7372
7372
  async (assignment) => {
@@ -7979,7 +7979,7 @@ function buildHandler(tool, pseudonymizer) {
7979
7979
  }
7980
7980
  return response;
7981
7981
  } catch (error) {
7982
- if (!(error instanceof _chunk26UC2T3Rcjs.CanvasApiError)) {
7982
+ if (!(error instanceof _chunkUXJUBCTVcjs.CanvasApiError)) {
7983
7983
  console.error(`Unexpected error in tool "${tool.name}":`, error);
7984
7984
  }
7985
7985
  return {
@@ -8067,7 +8067,7 @@ function registerSyllabusResource(server, canvas) {
8067
8067
  ]
8068
8068
  };
8069
8069
  } catch (error) {
8070
- if (!(error instanceof _chunk26UC2T3Rcjs.CanvasApiError)) {
8070
+ if (!(error instanceof _chunkUXJUBCTVcjs.CanvasApiError)) {
8071
8071
  console.error("Unexpected error in syllabus resource:", error);
8072
8072
  }
8073
8073
  return {
@@ -8121,7 +8121,7 @@ function registerAssignmentDescriptionResource(server, canvas) {
8121
8121
  ]
8122
8122
  };
8123
8123
  } catch (error) {
8124
- if (!(error instanceof _chunk26UC2T3Rcjs.CanvasApiError)) {
8124
+ if (!(error instanceof _chunkUXJUBCTVcjs.CanvasApiError)) {
8125
8125
  console.error("Unexpected error in assignment-description resource:", error);
8126
8126
  }
8127
8127
  return {
@@ -9521,14 +9521,14 @@ function resolvePseudonymizer(config) {
9521
9521
  });
9522
9522
  }
9523
9523
  function createCanvasMCPServer(config) {
9524
- const canvas = new (0, _chunkDELZA7F6cjs.CanvasClient)({
9524
+ const canvas = new (0, _chunkJXM4QLYVcjs.CanvasClient)({
9525
9525
  token: config.token,
9526
9526
  baseUrl: config.baseUrl
9527
9527
  });
9528
9528
  const pseudonymizer = resolvePseudonymizer(config);
9529
9529
  const server = new (0, _mcpjs.McpServer)({
9530
9530
  name: "canvas-lms-mcp",
9531
- version: _chunk26UC2T3Rcjs.version
9531
+ version: _chunkUXJUBCTVcjs.version
9532
9532
  });
9533
9533
  const features = {
9534
9534
  assignmentSubmission: config.enableAssignmentSubmission,
@@ -9549,4 +9549,4 @@ function createCanvasMCPServer(config) {
9549
9549
 
9550
9550
 
9551
9551
  exports.Pseudonymizer = Pseudonymizer; exports.createSharedPseudonymizer = createSharedPseudonymizer; exports.createCanvasMCPServer = createCanvasMCPServer;
9552
- //# sourceMappingURL=chunk-KS6CBFHY.cjs.map
9552
+ //# sourceMappingURL=chunk-JD4QGC5S.cjs.map