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.
- package/bin/canvas-lms-mcp.js +24 -1
- package/dist/canvas/index.cjs +3 -3
- package/dist/canvas/index.js +2 -2
- package/dist/{chunk-LYNXEFQN.js → chunk-6FRP3JSU.js} +2 -2
- package/dist/{chunk-LYNXEFQN.js.map → chunk-6FRP3JSU.js.map} +1 -1
- package/dist/{chunk-5WTLGWRA.js → chunk-B44OQYTW.js} +3 -3
- package/dist/{chunk-KS6CBFHY.cjs → chunk-JD4QGC5S.cjs} +42 -42
- package/dist/{chunk-KS6CBFHY.cjs.map → chunk-JD4QGC5S.cjs.map} +1 -1
- package/dist/{chunk-DELZA7F6.cjs → chunk-JXM4QLYV.cjs} +4 -4
- package/dist/{chunk-DELZA7F6.cjs.map → chunk-JXM4QLYV.cjs.map} +1 -1
- package/dist/{chunk-H3LDGHYB.js → chunk-UXGS7LAH.js} +2 -2
- package/dist/{chunk-26UC2T3R.cjs → chunk-UXJUBCTV.cjs} +2 -2
- package/dist/{chunk-26UC2T3R.cjs.map → chunk-UXJUBCTV.cjs.map} +1 -1
- package/dist/http.cjs +6 -6
- package/dist/http.js +3 -3
- package/dist/init.cjs +3 -3
- package/dist/init.js +1 -1
- package/dist/server.cjs +4 -4
- package/dist/server.js +3 -3
- package/dist/stdio.cjs +4 -4
- package/dist/stdio.js +3 -3
- package/package.json +1 -1
- /package/dist/{chunk-5WTLGWRA.js.map → chunk-B44OQYTW.js.map} +0 -0
- /package/dist/{chunk-H3LDGHYB.js.map → chunk-UXGS7LAH.js.map} +0 -0
package/bin/canvas-lms-mcp.js
CHANGED
|
@@ -2,7 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
const sub = process.argv[2]
|
|
4
4
|
|
|
5
|
-
|
|
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')
|
package/dist/canvas/index.cjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkJXM4QLYVcjs = require('../chunk-JXM4QLYV.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
var
|
|
9
|
+
var _chunkUXJUBCTVcjs = require('../chunk-UXJUBCTV.cjs');
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
exports.CanvasApiError =
|
|
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
|
package/dist/canvas/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CanvasClient
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-UXGS7LAH.js";
|
|
4
4
|
import {
|
|
5
5
|
CanvasApiError,
|
|
6
6
|
CanvasHttpClient,
|
|
7
7
|
appendCanvasQuery,
|
|
8
8
|
toCanvasQuery
|
|
9
|
-
} from "../chunk-
|
|
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.
|
|
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-
|
|
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":"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","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":";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"]}
|
|
@@ -21,11 +21,11 @@ import {
|
|
|
21
21
|
OUTCOME_SORT_ORDER,
|
|
22
22
|
SEARCH_CONTENT_TYPES,
|
|
23
23
|
mapWithConcurrency
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-UXGS7LAH.js";
|
|
25
25
|
import {
|
|
26
26
|
CanvasApiError,
|
|
27
27
|
version
|
|
28
|
-
} from "./chunk-
|
|
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-
|
|
9552
|
+
//# sourceMappingURL=chunk-B44OQYTW.js.map
|
|
@@ -21,11 +21,11 @@ var _chunkQCEGMTXWcjs = require('./chunk-QCEGMTXW.cjs');
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
var
|
|
24
|
+
var _chunkJXM4QLYVcjs = require('./chunk-JXM4QLYV.cjs');
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
var
|
|
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
|
|
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(
|
|
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
|
-
...
|
|
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
|
|
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
|
|
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
|
|
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
|
|
4000
|
+
if (err instanceof _chunkUXJUBCTVcjs.CanvasApiError && err.status === 403) {
|
|
4001
4001
|
policyUnavailable = true;
|
|
4002
|
-
} else if (err instanceof
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
4553
|
-
outcome_group_style: _zod.z.enum(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
4683
|
-
sort_by: _zod.z.enum(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
6126
|
-
message: result.err instanceof
|
|
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
|
|
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
|
|
6290
|
-
message: r.err instanceof
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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:
|
|
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-
|
|
9552
|
+
//# sourceMappingURL=chunk-JD4QGC5S.cjs.map
|