canvas-lms-mcp 1.27.7 → 1.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/canvas/index.cjs +3 -3
- package/dist/canvas/index.js +2 -2
- package/dist/{chunk-5ALUPLEK.cjs → chunk-BJFRBBXI.cjs} +114 -41
- package/dist/chunk-BJFRBBXI.cjs.map +1 -0
- package/dist/{chunk-XTYTTVRA.js → chunk-EY7MUK6A.js} +2 -2
- package/dist/{chunk-XTYTTVRA.js.map → chunk-EY7MUK6A.js.map} +1 -1
- package/dist/{chunk-U7JB6QE2.js → chunk-FF7TOBVJ.js} +2 -2
- package/dist/{chunk-SYYAP7AS.cjs → chunk-FXFEJU2S.cjs} +4 -4
- package/dist/{chunk-SYYAP7AS.cjs.map → chunk-FXFEJU2S.cjs.map} +1 -1
- package/dist/{chunk-3TMKZNKA.js → chunk-MZGRCYAP.js} +348 -275
- package/dist/chunk-MZGRCYAP.js.map +1 -0
- package/dist/{chunk-NRDHKYUE.cjs → chunk-V7NUQZO2.cjs} +2 -2
- package/dist/{chunk-NRDHKYUE.cjs.map → chunk-V7NUQZO2.cjs.map} +1 -1
- package/dist/discovery/manifests.d.ts +9 -1
- package/dist/discovery/manifests.d.ts.map +1 -1
- package/dist/http.cjs +5 -5
- 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/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/output/contract.d.ts +76 -0
- package/dist/tools/output/contract.d.ts.map +1 -0
- package/dist/tools/output/entities.d.ts +46 -0
- package/dist/tools/output/entities.d.ts.map +1 -0
- package/dist/tools/pages.d.ts.map +1 -1
- package/dist/tools/types.d.ts +12 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-3TMKZNKA.js.map +0 -1
- package/dist/chunk-5ALUPLEK.cjs.map +0 -1
- /package/dist/{chunk-U7JB6QE2.js.map → chunk-FF7TOBVJ.js.map} +0 -0
package/README.md
CHANGED
|
@@ -165,6 +165,33 @@ Canvas applies rate limits per-user. When creating many New Quizzes items (e.g.,
|
|
|
165
165
|
| Course Syllabus | `canvas://course/{courseId}/syllabus` | text/html |
|
|
166
166
|
| Assignment Description | `canvas://course/{courseId}/assignment/{assignmentId}/description` | text/html |
|
|
167
167
|
|
|
168
|
+
### Structured output
|
|
169
|
+
|
|
170
|
+
Some tools return machine-readable `structuredContent` alongside their text content, validated against an `outputSchema` the server advertises in `tools/list`. Migration is **per tool**, so the two surfaces coexist:
|
|
171
|
+
|
|
172
|
+
| Client behaviour | Migrated tool | Unmigrated tool |
|
|
173
|
+
|---|---|---|
|
|
174
|
+
| Reads `content[0].text` | Works, byte-identical to before | Works |
|
|
175
|
+
| Reads `structuredContent` | Works | Field is absent |
|
|
176
|
+
| Validates against `outputSchema` | Works | No schema advertised |
|
|
177
|
+
|
|
178
|
+
Three guarantees hold for every migrated tool:
|
|
179
|
+
|
|
180
|
+
- **The text content is unchanged.** `content[0].text` carries exactly the bytes it did before migration. `structuredContent` is added alongside it, never in place of it, so text-only clients and the interactive widgets are unaffected.
|
|
181
|
+
- **Canvas fields we do not declare are passed through, not stripped or rejected.** Entity schemas are open, because Canvas ships new fields continuously and a closed schema would turn each one into a failed tool call. Only the envelopes this server authors itself are closed.
|
|
182
|
+
- **Errors are never structured.** A tool failure returns `isError: true` with plain text, exactly as before.
|
|
183
|
+
|
|
184
|
+
A tool returning a list wraps it under a single plural key, since MCP requires an output schema to be an object:
|
|
185
|
+
|
|
186
|
+
```jsonc
|
|
187
|
+
{
|
|
188
|
+
"content": [{ "type": "text", "text": "[ ... unchanged JSON ... ]" }],
|
|
189
|
+
"structuredContent": { "pages": [ /* the same value */ ] }
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Which tools are migrated is recorded per tool as `structuredOutput` in [`docs/generated/tool-manifest.json`](docs/generated/tool-manifest.json) (manifest schema 1.1). Currently: the five `pages` tools.
|
|
194
|
+
|
|
168
195
|
### Interactive widgets
|
|
169
196
|
|
|
170
197
|
`view_course_structure` is an [MCP Apps](https://github.com/modelcontextprotocol/ext-apps) tool: hosts that support the spec render an interactive tree explorer (collapsible modules, type-filter chips, title search, published/unpublished badges, links open in a new tab); hosts that don't fall back transparently to the same JSON payload that `get_course_structure` returns. The widget is self-contained — no external scripts, fonts, or network calls — and is shipped inline with the tool definition.
|
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 _chunkFXFEJU2Scjs = require('../chunk-FXFEJU2S.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
var
|
|
9
|
+
var _chunkV7NUQZO2cjs = require('../chunk-V7NUQZO2.cjs');
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
exports.CanvasApiError =
|
|
16
|
+
exports.CanvasApiError = _chunkV7NUQZO2cjs.CanvasApiError; exports.CanvasClient = _chunkFXFEJU2Scjs.CanvasClient; exports.CanvasHttpClient = _chunkV7NUQZO2cjs.CanvasHttpClient; exports.appendCanvasQuery = _chunkV7NUQZO2cjs.appendCanvasQuery; exports.toCanvasQuery = _chunkV7NUQZO2cjs.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-FF7TOBVJ.js";
|
|
4
4
|
import {
|
|
5
5
|
CanvasApiError,
|
|
6
6
|
CanvasHttpClient,
|
|
7
7
|
appendCanvasQuery,
|
|
8
8
|
toCanvasQuery
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-EY7MUK6A.js";
|
|
10
10
|
export {
|
|
11
11
|
CanvasApiError,
|
|
12
12
|
CanvasClient,
|
|
@@ -15,11 +15,11 @@ var _chunkOOP4MJA7cjs = require('./chunk-OOP4MJA7.cjs');
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
var
|
|
18
|
+
var _chunkFXFEJU2Scjs = require('./chunk-FXFEJU2S.cjs');
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
var
|
|
22
|
+
var _chunkV7NUQZO2cjs = require('./chunk-V7NUQZO2.cjs');
|
|
23
23
|
|
|
24
24
|
// src/server.ts
|
|
25
25
|
var _mcpjs = require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
@@ -921,7 +921,7 @@ function assignmentSubmissionTools(canvas) {
|
|
|
921
921
|
|
|
922
922
|
// src/tools/errors.ts
|
|
923
923
|
function formatError(error) {
|
|
924
|
-
if (error instanceof
|
|
924
|
+
if (error instanceof _chunkV7NUQZO2cjs.CanvasApiError) {
|
|
925
925
|
const status = error.status;
|
|
926
926
|
const message = error.message;
|
|
927
927
|
const ltiMarkers = ["Tool not configured", "tool is not configured"];
|
|
@@ -975,7 +975,7 @@ function analyticsTools(canvas) {
|
|
|
975
975
|
inputSchema: {
|
|
976
976
|
course_id: _zod.z.number().describe("The Canvas course ID"),
|
|
977
977
|
search_term: _zod.z.string().describe("The keyword or phrase to search for"),
|
|
978
|
-
content_types: _zod.z.array(_zod.z.enum(
|
|
978
|
+
content_types: _zod.z.array(_zod.z.enum(_chunkFXFEJU2Scjs.SEARCH_CONTENT_TYPES)).optional().describe(
|
|
979
979
|
"Content types to search. Defaults to all types: pages, discussions, assignments, announcements."
|
|
980
980
|
)
|
|
981
981
|
},
|
|
@@ -987,7 +987,7 @@ function analyticsTools(canvas) {
|
|
|
987
987
|
const courseId = params.course_id;
|
|
988
988
|
const searchTerm = params.search_term;
|
|
989
989
|
const types = _nullishCoalesce(params.content_types, () => ( [
|
|
990
|
-
...
|
|
990
|
+
..._chunkFXFEJU2Scjs.SEARCH_CONTENT_TYPES
|
|
991
991
|
]));
|
|
992
992
|
if (types.length === 0) return { results: [] };
|
|
993
993
|
const settled = await Promise.allSettled(
|
|
@@ -1240,7 +1240,7 @@ function attentionTools(canvas, pseudonymizer) {
|
|
|
1240
1240
|
summaryMap.set(s.id, s);
|
|
1241
1241
|
}
|
|
1242
1242
|
} catch (err) {
|
|
1243
|
-
if (err instanceof
|
|
1243
|
+
if (err instanceof _chunkV7NUQZO2cjs.CanvasApiError && err.status === 404) {
|
|
1244
1244
|
analyticsAvailable = false;
|
|
1245
1245
|
} else {
|
|
1246
1246
|
throw err;
|
|
@@ -1584,7 +1584,7 @@ async function fanOut(config) {
|
|
|
1584
1584
|
skipped.push(outcome.result);
|
|
1585
1585
|
}
|
|
1586
1586
|
} catch (err) {
|
|
1587
|
-
if (!(err instanceof
|
|
1587
|
+
if (!(err instanceof _chunkV7NUQZO2cjs.CanvasApiError)) {
|
|
1588
1588
|
console.error(`Unexpected error ${config.errorContext(item)}:`, err);
|
|
1589
1589
|
}
|
|
1590
1590
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -3885,9 +3885,9 @@ function gradingPolicyTools(canvas) {
|
|
|
3885
3885
|
latePolicySource = "api";
|
|
3886
3886
|
} else {
|
|
3887
3887
|
const err = latePolicyResult.reason;
|
|
3888
|
-
if (err instanceof
|
|
3888
|
+
if (err instanceof _chunkV7NUQZO2cjs.CanvasApiError && err.status === 403) {
|
|
3889
3889
|
policyUnavailable = true;
|
|
3890
|
-
} else if (err instanceof
|
|
3890
|
+
} else if (err instanceof _chunkV7NUQZO2cjs.CanvasApiError && err.status === 404) {
|
|
3891
3891
|
latePolicySource = "default";
|
|
3892
3892
|
} else {
|
|
3893
3893
|
throw err;
|
|
@@ -3917,7 +3917,7 @@ function gradingPolicyTools(canvas) {
|
|
|
3917
3917
|
}
|
|
3918
3918
|
}
|
|
3919
3919
|
} catch (err) {
|
|
3920
|
-
if (!(err instanceof
|
|
3920
|
+
if (!(err instanceof _chunkV7NUQZO2cjs.CanvasApiError)) throw err;
|
|
3921
3921
|
if (err.status !== 403 && err.status !== 404) throw err;
|
|
3922
3922
|
}
|
|
3923
3923
|
if (standardTitle === null) {
|
|
@@ -4047,7 +4047,7 @@ function gradingStandardsTools(canvas) {
|
|
|
4047
4047
|
}
|
|
4048
4048
|
throw new Error("Provide either course_id or account_id.");
|
|
4049
4049
|
} catch (error) {
|
|
4050
|
-
if (error instanceof
|
|
4050
|
+
if (error instanceof _chunkV7NUQZO2cjs.CanvasApiError && error.status === 403 && courseId === void 0 && accountId !== void 0 && !error.message.toLowerCase().includes("rate limit exceeded")) {
|
|
4051
4051
|
throw new Error(
|
|
4052
4052
|
"Creating grading standards at the account level requires Canvas admin permissions. Try creating the standard in a course context instead (use course_id).",
|
|
4053
4053
|
{ cause: error }
|
|
@@ -4358,7 +4358,7 @@ function outcomeTools(canvas, pseudonymizer) {
|
|
|
4358
4358
|
title: "Get Root Outcome Group",
|
|
4359
4359
|
description: "Get the root outcome group for an account or course context.",
|
|
4360
4360
|
inputSchema: {
|
|
4361
|
-
context_type: _zod.z.enum(
|
|
4361
|
+
context_type: _zod.z.enum(_chunkFXFEJU2Scjs.OUTCOME_CONTEXT_TYPES).describe("Whether to read outcomes from an account or course context."),
|
|
4362
4362
|
context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context.")
|
|
4363
4363
|
},
|
|
4364
4364
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
@@ -4372,7 +4372,7 @@ function outcomeTools(canvas, pseudonymizer) {
|
|
|
4372
4372
|
title: "List Outcome Groups",
|
|
4373
4373
|
description: "List all outcome groups for an account or course context.",
|
|
4374
4374
|
inputSchema: {
|
|
4375
|
-
context_type: _zod.z.enum(
|
|
4375
|
+
context_type: _zod.z.enum(_chunkFXFEJU2Scjs.OUTCOME_CONTEXT_TYPES).describe("Whether to read outcome groups from an account or course context."),
|
|
4376
4376
|
context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context.")
|
|
4377
4377
|
},
|
|
4378
4378
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
@@ -4386,10 +4386,10 @@ function outcomeTools(canvas, pseudonymizer) {
|
|
|
4386
4386
|
title: "List Outcome Group Links",
|
|
4387
4387
|
description: "List all outcome links in an account or course context.",
|
|
4388
4388
|
inputSchema: {
|
|
4389
|
-
context_type: _zod.z.enum(
|
|
4389
|
+
context_type: _zod.z.enum(_chunkFXFEJU2Scjs.OUTCOME_CONTEXT_TYPES).describe("Whether to read outcome links from an account or course context."),
|
|
4390
4390
|
context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
|
|
4391
|
-
outcome_style: _zod.z.enum(
|
|
4392
|
-
outcome_group_style: _zod.z.enum(
|
|
4391
|
+
outcome_style: _zod.z.enum(_chunkFXFEJU2Scjs.OUTCOME_DETAIL_LEVELS).optional().describe('Outcome detail level. Use "full" to include expanded outcome fields.'),
|
|
4392
|
+
outcome_group_style: _zod.z.enum(_chunkFXFEJU2Scjs.OUTCOME_DETAIL_LEVELS).optional().describe(
|
|
4393
4393
|
'Outcome group detail level. Use "full" to include expanded outcome group fields.'
|
|
4394
4394
|
)
|
|
4395
4395
|
},
|
|
@@ -4408,7 +4408,7 @@ function outcomeTools(canvas, pseudonymizer) {
|
|
|
4408
4408
|
title: "Get Outcome Group",
|
|
4409
4409
|
description: "Get details for a specific outcome group in an account or course context.",
|
|
4410
4410
|
inputSchema: {
|
|
4411
|
-
context_type: _zod.z.enum(
|
|
4411
|
+
context_type: _zod.z.enum(_chunkFXFEJU2Scjs.OUTCOME_CONTEXT_TYPES).describe("The outcome group context type."),
|
|
4412
4412
|
context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
|
|
4413
4413
|
outcome_group_id: _zod.z.number().describe("The Canvas outcome group ID.")
|
|
4414
4414
|
},
|
|
@@ -4424,10 +4424,10 @@ function outcomeTools(canvas, pseudonymizer) {
|
|
|
4424
4424
|
title: "List Outcome Group Outcomes",
|
|
4425
4425
|
description: "List the linked outcomes directly under a specific outcome group.",
|
|
4426
4426
|
inputSchema: {
|
|
4427
|
-
context_type: _zod.z.enum(
|
|
4427
|
+
context_type: _zod.z.enum(_chunkFXFEJU2Scjs.OUTCOME_CONTEXT_TYPES).describe("The outcome group context type."),
|
|
4428
4428
|
context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
|
|
4429
4429
|
outcome_group_id: _zod.z.number().describe("The Canvas outcome group ID."),
|
|
4430
|
-
outcome_style: _zod.z.enum(
|
|
4430
|
+
outcome_style: _zod.z.enum(_chunkFXFEJU2Scjs.OUTCOME_DETAIL_LEVELS).optional().describe('Outcome detail level. Use "full" to include expanded outcome fields.')
|
|
4431
4431
|
},
|
|
4432
4432
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
4433
4433
|
handler: async (params) => canvas.outcomes.listGroupOutcomes(
|
|
@@ -4444,7 +4444,7 @@ function outcomeTools(canvas, pseudonymizer) {
|
|
|
4444
4444
|
title: "List Outcome Group Subgroups",
|
|
4445
4445
|
description: "List the immediate child outcome groups under a specific outcome group.",
|
|
4446
4446
|
inputSchema: {
|
|
4447
|
-
context_type: _zod.z.enum(
|
|
4447
|
+
context_type: _zod.z.enum(_chunkFXFEJU2Scjs.OUTCOME_CONTEXT_TYPES).describe("The outcome group context type."),
|
|
4448
4448
|
context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
|
|
4449
4449
|
outcome_group_id: _zod.z.number().describe("The Canvas outcome group ID.")
|
|
4450
4450
|
},
|
|
@@ -4514,14 +4514,14 @@ function outcomeTools(canvas, pseudonymizer) {
|
|
|
4514
4514
|
inputSchema: {
|
|
4515
4515
|
course_id: _zod.z.number().describe("The Canvas course ID."),
|
|
4516
4516
|
aggregate: _zod.z.enum(["course"]).optional().describe("Aggregate all student rollups into a single course-level rollup."),
|
|
4517
|
-
aggregate_stat: _zod.z.enum(
|
|
4517
|
+
aggregate_stat: _zod.z.enum(_chunkFXFEJU2Scjs.OUTCOME_ROLLUP_AGGREGATE_STATS).optional().describe('Statistic to use when aggregate="course".'),
|
|
4518
4518
|
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:".'),
|
|
4519
4519
|
outcome_ids: _zod.z.array(_zod.z.number()).optional().describe("Optional outcome IDs to restrict the rollups."),
|
|
4520
4520
|
include_courses: _zod.z.boolean().optional().describe("Include linked course details in the response payload."),
|
|
4521
|
-
exclude: _zod.z.array(_zod.z.enum(
|
|
4522
|
-
sort_by: _zod.z.enum(
|
|
4521
|
+
exclude: _zod.z.array(_zod.z.enum(_chunkFXFEJU2Scjs.OUTCOME_EXCLUDE_OPTIONS)).optional().describe("Optional rollup exclusions for missing users or missing outcome results."),
|
|
4522
|
+
sort_by: _zod.z.enum(_chunkFXFEJU2Scjs.OUTCOME_ROLLUP_SORT_BY).optional().describe("Sort rollups by student name or by a specific outcome score."),
|
|
4523
4523
|
sort_outcome_id: _zod.z.number().optional().describe('Outcome ID to sort by when sort_by="outcome".'),
|
|
4524
|
-
sort_order: _zod.z.enum(
|
|
4524
|
+
sort_order: _zod.z.enum(_chunkFXFEJU2Scjs.OUTCOME_SORT_ORDER).optional().describe("Sort order to apply when sorting rollups."),
|
|
4525
4525
|
add_defaults: _zod.z.boolean().optional().describe("Include default mastery colors and levels when Canvas supports it.")
|
|
4526
4526
|
},
|
|
4527
4527
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
@@ -4571,7 +4571,7 @@ function outcomeTools(canvas, pseudonymizer) {
|
|
|
4571
4571
|
description: "Get mastery distribution analytics for outcomes in a course, optionally filtered by students or outcomes.",
|
|
4572
4572
|
inputSchema: {
|
|
4573
4573
|
course_id: _zod.z.number().describe("The Canvas course ID."),
|
|
4574
|
-
exclude: _zod.z.array(_zod.z.enum(
|
|
4574
|
+
exclude: _zod.z.array(_zod.z.enum(_chunkFXFEJU2Scjs.OUTCOME_EXCLUDE_OPTIONS)).optional().describe("Optional exclusions for missing users or missing outcome results."),
|
|
4575
4575
|
outcome_ids: _zod.z.array(_zod.z.number()).optional().describe("Optional outcome IDs to restrict the distribution results."),
|
|
4576
4576
|
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:".'),
|
|
4577
4577
|
include_alignment_distributions: _zod.z.boolean().optional().describe("Include contributing score distributions for alignments."),
|
|
@@ -4595,6 +4595,60 @@ function outcomeTools(canvas, pseudonymizer) {
|
|
|
4595
4595
|
|
|
4596
4596
|
// src/tools/pages.ts
|
|
4597
4597
|
|
|
4598
|
+
|
|
4599
|
+
// src/tools/output/contract.ts
|
|
4600
|
+
|
|
4601
|
+
var ACK_MESSAGE = "Operation completed successfully.";
|
|
4602
|
+
function objectOutput(schema) {
|
|
4603
|
+
return { kind: "object", schema };
|
|
4604
|
+
}
|
|
4605
|
+
function listOutput(key, entity) {
|
|
4606
|
+
return { kind: "list", key, schema: _zod.z.strictObject({ [key]: _zod.z.array(entity) }) };
|
|
4607
|
+
}
|
|
4608
|
+
function buildEnvelope(contract, value) {
|
|
4609
|
+
switch (contract.kind) {
|
|
4610
|
+
case "list":
|
|
4611
|
+
return { [contract.key]: value };
|
|
4612
|
+
case "ack":
|
|
4613
|
+
return { ok: true, message: ACK_MESSAGE };
|
|
4614
|
+
case "object":
|
|
4615
|
+
return value;
|
|
4616
|
+
}
|
|
4617
|
+
}
|
|
4618
|
+
function validateEnvelope(contract, envelope) {
|
|
4619
|
+
const parsed = contract.schema.safeParse(envelope);
|
|
4620
|
+
if (parsed.success) return { ok: true };
|
|
4621
|
+
return {
|
|
4622
|
+
ok: false,
|
|
4623
|
+
issues: parsed.error.issues.map((issue) => {
|
|
4624
|
+
const path = issue.path.join(".");
|
|
4625
|
+
return path === "" ? issue.message : `${path}: ${issue.message}`;
|
|
4626
|
+
})
|
|
4627
|
+
};
|
|
4628
|
+
}
|
|
4629
|
+
function outputContractError(toolName) {
|
|
4630
|
+
return `Canvas returned data this tool could not describe (${toolName}). This is a canvas-lms-mcp bug, not a Canvas permission problem \u2014 please report it. Re-run with CANVAS_LOG_LEVEL=debug for detail.`;
|
|
4631
|
+
}
|
|
4632
|
+
|
|
4633
|
+
// src/tools/output/entities.ts
|
|
4634
|
+
|
|
4635
|
+
var canvasPageSchema = _zod.z.looseObject({
|
|
4636
|
+
page_id: _zod.z.number(),
|
|
4637
|
+
url: _zod.z.string(),
|
|
4638
|
+
title: _zod.z.string(),
|
|
4639
|
+
// Absent on the paginated list endpoint, which returns stubs without a body.
|
|
4640
|
+
body: _zod.z.string().nullish(),
|
|
4641
|
+
published: _zod.z.boolean().nullish(),
|
|
4642
|
+
created_at: _zod.z.string().nullish(),
|
|
4643
|
+
updated_at: _zod.z.string().nullish(),
|
|
4644
|
+
editing_roles: _zod.z.string().nullish()
|
|
4645
|
+
});
|
|
4646
|
+
var pageDeletionSchema = _zod.z.strictObject({
|
|
4647
|
+
deleted: _zod.z.literal(true),
|
|
4648
|
+
page_url: _zod.z.string()
|
|
4649
|
+
});
|
|
4650
|
+
|
|
4651
|
+
// src/tools/pages.ts
|
|
4598
4652
|
function pageTools(canvas) {
|
|
4599
4653
|
return [
|
|
4600
4654
|
{
|
|
@@ -4605,6 +4659,7 @@ function pageTools(canvas) {
|
|
|
4605
4659
|
inputSchema: {
|
|
4606
4660
|
course_id: _zod.z.number().describe("The Canvas course ID")
|
|
4607
4661
|
},
|
|
4662
|
+
output: listOutput("pages", canvasPageSchema),
|
|
4608
4663
|
annotations: {
|
|
4609
4664
|
readOnlyHint: true,
|
|
4610
4665
|
openWorldHint: true
|
|
@@ -4623,6 +4678,7 @@ function pageTools(canvas) {
|
|
|
4623
4678
|
course_id: _zod.z.number().describe("The Canvas course ID"),
|
|
4624
4679
|
page_url: _zod.z.string().describe('The page URL slug (e.g. "welcome-page")')
|
|
4625
4680
|
},
|
|
4681
|
+
output: objectOutput(canvasPageSchema),
|
|
4626
4682
|
annotations: {
|
|
4627
4683
|
readOnlyHint: true,
|
|
4628
4684
|
openWorldHint: true
|
|
@@ -4644,6 +4700,7 @@ function pageTools(canvas) {
|
|
|
4644
4700
|
published: _zod.z.boolean().optional().describe("Whether the page is published"),
|
|
4645
4701
|
editing_roles: _zod.z.string().optional().describe('Who can edit: "teachers", "students", "members", or "public"')
|
|
4646
4702
|
},
|
|
4703
|
+
output: objectOutput(canvasPageSchema),
|
|
4647
4704
|
annotations: {
|
|
4648
4705
|
destructiveHint: true,
|
|
4649
4706
|
openWorldHint: true
|
|
@@ -4670,6 +4727,7 @@ function pageTools(canvas) {
|
|
|
4670
4727
|
published: _zod.z.boolean().optional().describe("Whether the page is published"),
|
|
4671
4728
|
editing_roles: _zod.z.string().optional().describe('Who can edit: "teachers", "students", "members", or "public"')
|
|
4672
4729
|
},
|
|
4730
|
+
output: objectOutput(canvasPageSchema),
|
|
4673
4731
|
annotations: {
|
|
4674
4732
|
destructiveHint: true,
|
|
4675
4733
|
openWorldHint: true
|
|
@@ -4693,6 +4751,7 @@ function pageTools(canvas) {
|
|
|
4693
4751
|
course_id: _zod.z.number().describe("The Canvas course ID"),
|
|
4694
4752
|
page_url: _zod.z.string().describe("The page URL slug to delete")
|
|
4695
4753
|
},
|
|
4754
|
+
output: objectOutput(pageDeletionSchema),
|
|
4696
4755
|
annotations: {
|
|
4697
4756
|
destructiveHint: true,
|
|
4698
4757
|
openWorldHint: true
|
|
@@ -5292,7 +5351,7 @@ function newQuizAccommodationTools(canvas) {
|
|
|
5292
5351
|
extra_attempts: record.extra_attempts
|
|
5293
5352
|
});
|
|
5294
5353
|
} catch (err) {
|
|
5295
|
-
const message = err instanceof
|
|
5354
|
+
const message = err instanceof _chunkV7NUQZO2cjs.CanvasApiError ? err.message : "Unknown error";
|
|
5296
5355
|
failed.push({
|
|
5297
5356
|
assignment_id: assignmentId,
|
|
5298
5357
|
time_multiplier: null,
|
|
@@ -5832,8 +5891,8 @@ function studentTools(canvas, pseudonymizer) {
|
|
|
5832
5891
|
} else {
|
|
5833
5892
|
coursesFailed.push({
|
|
5834
5893
|
course_id: result.courseId,
|
|
5835
|
-
status: result.err instanceof
|
|
5836
|
-
message: result.err instanceof
|
|
5894
|
+
status: result.err instanceof _chunkV7NUQZO2cjs.CanvasApiError ? result.err.status : null,
|
|
5895
|
+
message: result.err instanceof _chunkV7NUQZO2cjs.CanvasApiError ? result.err.message : String(result.err)
|
|
5837
5896
|
});
|
|
5838
5897
|
}
|
|
5839
5898
|
}
|
|
@@ -5973,7 +6032,7 @@ function studentSearchTools(canvas, pseudonymizer) {
|
|
|
5973
6032
|
const coursesToScan = truncated ? sorted.slice(0, maxCourses) : sorted;
|
|
5974
6033
|
const coursesFailed = [];
|
|
5975
6034
|
const perCourseMatches = [];
|
|
5976
|
-
const scanResults = await
|
|
6035
|
+
const scanResults = await _chunkFXFEJU2Scjs.mapWithConcurrency.call(void 0,
|
|
5977
6036
|
coursesToScan,
|
|
5978
6037
|
CONCURRENT_COURSE_LIMIT,
|
|
5979
6038
|
async (course) => {
|
|
@@ -5996,8 +6055,8 @@ function studentSearchTools(canvas, pseudonymizer) {
|
|
|
5996
6055
|
} else {
|
|
5997
6056
|
coursesFailed.push({
|
|
5998
6057
|
course_id: r.course.id,
|
|
5999
|
-
status: r.err instanceof
|
|
6000
|
-
message: r.err instanceof
|
|
6058
|
+
status: r.err instanceof _chunkV7NUQZO2cjs.CanvasApiError ? r.err.status : null,
|
|
6059
|
+
message: r.err instanceof _chunkV7NUQZO2cjs.CanvasApiError ? r.err.message : String(r.err)
|
|
6001
6060
|
});
|
|
6002
6061
|
}
|
|
6003
6062
|
}
|
|
@@ -6675,7 +6734,7 @@ async function scanQuizzes(canvas, courseId, warnings) {
|
|
|
6675
6734
|
canvas.assignments.list(courseId)
|
|
6676
6735
|
]);
|
|
6677
6736
|
const classicQuizzes = quizzes.filter((quiz) => CLASSIC_QUIZ_TYPES3.has(quiz.quiz_type));
|
|
6678
|
-
const classicFindings = await
|
|
6737
|
+
const classicFindings = await _chunkFXFEJU2Scjs.mapWithConcurrency.call(void 0,
|
|
6679
6738
|
classicQuizzes,
|
|
6680
6739
|
QUIZ_SCAN_CONCURRENCY_LIMIT,
|
|
6681
6740
|
async (quiz) => {
|
|
@@ -6701,7 +6760,7 @@ async function scanQuizzes(canvas, courseId, warnings) {
|
|
|
6701
6760
|
}
|
|
6702
6761
|
);
|
|
6703
6762
|
const newQuizAssignments = assignments.filter((a) => a.is_quiz_lti_assignment === true);
|
|
6704
|
-
const newQuizFindings = await
|
|
6763
|
+
const newQuizFindings = await _chunkFXFEJU2Scjs.mapWithConcurrency.call(void 0,
|
|
6705
6764
|
newQuizAssignments,
|
|
6706
6765
|
QUIZ_SCAN_CONCURRENCY_LIMIT,
|
|
6707
6766
|
async (assignment) => {
|
|
@@ -7051,7 +7110,7 @@ async function scanQuizzesAccessibility(canvas, courseId, warnings) {
|
|
|
7051
7110
|
canvas.assignments.list(courseId)
|
|
7052
7111
|
]);
|
|
7053
7112
|
const classicQuizzes = quizzes.filter((quiz) => CLASSIC_QUIZ_TYPES4.has(quiz.quiz_type));
|
|
7054
|
-
const classicFindings = await
|
|
7113
|
+
const classicFindings = await _chunkFXFEJU2Scjs.mapWithConcurrency.call(void 0,
|
|
7055
7114
|
classicQuizzes,
|
|
7056
7115
|
QUIZ_SCAN_CONCURRENCY_LIMIT2,
|
|
7057
7116
|
async (quiz) => {
|
|
@@ -7076,7 +7135,7 @@ async function scanQuizzesAccessibility(canvas, courseId, warnings) {
|
|
|
7076
7135
|
}
|
|
7077
7136
|
);
|
|
7078
7137
|
const newQuizAssignments = assignments.filter((a) => a.is_quiz_lti_assignment === true);
|
|
7079
|
-
const newQuizFindings = await
|
|
7138
|
+
const newQuizFindings = await _chunkFXFEJU2Scjs.mapWithConcurrency.call(void 0,
|
|
7080
7139
|
newQuizAssignments,
|
|
7081
7140
|
QUIZ_SCAN_CONCURRENCY_LIMIT2,
|
|
7082
7141
|
async (assignment) => {
|
|
@@ -7662,6 +7721,18 @@ function buildHandler(tool, pseudonymizer) {
|
|
|
7662
7721
|
const response = {
|
|
7663
7722
|
content: [{ type: "text", text }]
|
|
7664
7723
|
};
|
|
7724
|
+
if (tool.output) {
|
|
7725
|
+
const structured = buildEnvelope(tool.output, value);
|
|
7726
|
+
const validation = validateEnvelope(tool.output, structured);
|
|
7727
|
+
if (!validation.ok) {
|
|
7728
|
+
console.error(`Output contract violation in "${tool.name}":`, validation.issues);
|
|
7729
|
+
return {
|
|
7730
|
+
content: [{ type: "text", text: outputContractError(tool.name) }],
|
|
7731
|
+
isError: true
|
|
7732
|
+
};
|
|
7733
|
+
}
|
|
7734
|
+
response.structuredContent = structured;
|
|
7735
|
+
}
|
|
7665
7736
|
if (_optionalChain([pseudonymizer, 'optionalAccess', _144 => _144.isEnabled, 'call', _145 => _145()])) {
|
|
7666
7737
|
response._meta = { pseudonymized: true, note: PSEUDONYM_META_NOTE };
|
|
7667
7738
|
}
|
|
@@ -7673,7 +7744,7 @@ function buildHandler(tool, pseudonymizer) {
|
|
|
7673
7744
|
}
|
|
7674
7745
|
return response;
|
|
7675
7746
|
} catch (error) {
|
|
7676
|
-
if (!(error instanceof
|
|
7747
|
+
if (!(error instanceof _chunkV7NUQZO2cjs.CanvasApiError)) {
|
|
7677
7748
|
console.error(`Unexpected error in tool "${tool.name}":`, error);
|
|
7678
7749
|
}
|
|
7679
7750
|
return {
|
|
@@ -7695,6 +7766,7 @@ function registerAllTools(server, canvas, pseudonymizer, role, features) {
|
|
|
7695
7766
|
title: tool.title,
|
|
7696
7767
|
description: tool.description,
|
|
7697
7768
|
inputSchema: tool.inputSchema,
|
|
7769
|
+
...tool.output ? { outputSchema: tool.output.schema } : {},
|
|
7698
7770
|
annotations: tool.annotations,
|
|
7699
7771
|
_meta: { ui: { resourceUri: tool.ui.resourceUri } }
|
|
7700
7772
|
},
|
|
@@ -7707,6 +7779,7 @@ function registerAllTools(server, canvas, pseudonymizer, role, features) {
|
|
|
7707
7779
|
title: tool.title,
|
|
7708
7780
|
description: tool.description,
|
|
7709
7781
|
inputSchema: tool.inputSchema,
|
|
7782
|
+
...tool.output ? { outputSchema: tool.output.schema } : {},
|
|
7710
7783
|
annotations: tool.annotations
|
|
7711
7784
|
},
|
|
7712
7785
|
handler
|
|
@@ -7758,7 +7831,7 @@ function registerSyllabusResource(server, canvas) {
|
|
|
7758
7831
|
]
|
|
7759
7832
|
};
|
|
7760
7833
|
} catch (error) {
|
|
7761
|
-
if (!(error instanceof
|
|
7834
|
+
if (!(error instanceof _chunkV7NUQZO2cjs.CanvasApiError)) {
|
|
7762
7835
|
console.error("Unexpected error in syllabus resource:", error);
|
|
7763
7836
|
}
|
|
7764
7837
|
return {
|
|
@@ -7812,7 +7885,7 @@ function registerAssignmentDescriptionResource(server, canvas) {
|
|
|
7812
7885
|
]
|
|
7813
7886
|
};
|
|
7814
7887
|
} catch (error) {
|
|
7815
|
-
if (!(error instanceof
|
|
7888
|
+
if (!(error instanceof _chunkV7NUQZO2cjs.CanvasApiError)) {
|
|
7816
7889
|
console.error("Unexpected error in assignment-description resource:", error);
|
|
7817
7890
|
}
|
|
7818
7891
|
return {
|
|
@@ -8864,14 +8937,14 @@ function registerAllResources(server, canvas) {
|
|
|
8864
8937
|
|
|
8865
8938
|
// src/server.ts
|
|
8866
8939
|
function createCanvasMCPServer(config) {
|
|
8867
|
-
const canvas = new (0,
|
|
8940
|
+
const canvas = new (0, _chunkFXFEJU2Scjs.CanvasClient)({
|
|
8868
8941
|
token: config.token,
|
|
8869
8942
|
baseUrl: config.baseUrl
|
|
8870
8943
|
});
|
|
8871
8944
|
const pseudonymizer = _nullishCoalesce(config.pseudonymizer, () => ( new Pseudonymizer({ baseUrl: config.baseUrl })));
|
|
8872
8945
|
const server = new (0, _mcpjs.McpServer)({
|
|
8873
8946
|
name: "canvas-lms-mcp",
|
|
8874
|
-
version:
|
|
8947
|
+
version: _chunkV7NUQZO2cjs.version
|
|
8875
8948
|
});
|
|
8876
8949
|
const features = {
|
|
8877
8950
|
assignmentSubmission: config.enableAssignmentSubmission
|
|
@@ -8885,4 +8958,4 @@ function createCanvasMCPServer(config) {
|
|
|
8885
8958
|
|
|
8886
8959
|
|
|
8887
8960
|
exports.Pseudonymizer = Pseudonymizer; exports.createCanvasMCPServer = createCanvasMCPServer;
|
|
8888
|
-
//# sourceMappingURL=chunk-
|
|
8961
|
+
//# sourceMappingURL=chunk-BJFRBBXI.cjs.map
|