canvas-lms-mcp 1.3.0 → 1.4.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.
@@ -13,6 +13,104 @@ var _chunkXAOI3FO6cjs = require('./chunk-XAOI3FO6.cjs');
13
13
  // src/server.ts
14
14
  var _mcpjs = require('@modelcontextprotocol/sdk/server/mcp.js');
15
15
 
16
+ // src/tools/accounts.ts
17
+ var _zod = require('zod');
18
+ function accountTools(canvas) {
19
+ return [
20
+ {
21
+ name: "get_account",
22
+ description: "Get details for a Canvas account by ID.",
23
+ inputSchema: {
24
+ account_id: _zod.z.number().describe("The Canvas account ID")
25
+ },
26
+ annotations: {
27
+ readOnlyHint: true,
28
+ openWorldHint: true
29
+ },
30
+ handler: async (params) => {
31
+ return canvas.accounts.get(params.account_id);
32
+ }
33
+ },
34
+ {
35
+ name: "list_accounts",
36
+ description: "List all accounts accessible to the authenticated user.",
37
+ inputSchema: {},
38
+ annotations: {
39
+ readOnlyHint: true,
40
+ openWorldHint: true
41
+ },
42
+ handler: async () => {
43
+ return canvas.accounts.list();
44
+ }
45
+ },
46
+ {
47
+ name: "list_sub_accounts",
48
+ description: "List sub-accounts under a given Canvas account.",
49
+ inputSchema: {
50
+ account_id: _zod.z.number().describe("The parent Canvas account ID")
51
+ },
52
+ annotations: {
53
+ readOnlyHint: true,
54
+ openWorldHint: true
55
+ },
56
+ handler: async (params) => {
57
+ return canvas.accounts.listSubAccounts(params.account_id);
58
+ }
59
+ },
60
+ {
61
+ name: "list_account_courses",
62
+ description: "List courses under a given Canvas account.",
63
+ inputSchema: {
64
+ account_id: _zod.z.number().describe("The Canvas account ID"),
65
+ search_term: _zod.z.string().optional().describe("Search courses by name or course code")
66
+ },
67
+ annotations: {
68
+ readOnlyHint: true,
69
+ openWorldHint: true
70
+ },
71
+ handler: async (params) => {
72
+ return canvas.accounts.listCourses(params.account_id, {
73
+ search_term: params.search_term
74
+ });
75
+ }
76
+ },
77
+ {
78
+ name: "list_account_users",
79
+ description: "List users in a Canvas account.",
80
+ inputSchema: {
81
+ account_id: _zod.z.number().describe("The Canvas account ID"),
82
+ search_term: _zod.z.string().optional().describe("Search users by name, email, or login")
83
+ },
84
+ annotations: {
85
+ readOnlyHint: true,
86
+ openWorldHint: true
87
+ },
88
+ handler: async (params) => {
89
+ return canvas.accounts.listUsers(params.account_id, {
90
+ search_term: params.search_term
91
+ });
92
+ }
93
+ },
94
+ {
95
+ name: "get_account_reports",
96
+ description: "List available report types for a Canvas account.",
97
+ inputSchema: {
98
+ account_id: _zod.z.number().describe("The Canvas account ID")
99
+ },
100
+ annotations: {
101
+ readOnlyHint: true,
102
+ openWorldHint: true
103
+ },
104
+ handler: async (params) => {
105
+ return canvas.accounts.getReports(params.account_id);
106
+ }
107
+ }
108
+ ];
109
+ }
110
+
111
+ // src/tools/analytics.ts
112
+
113
+
16
114
  // src/tools/errors.ts
17
115
  function formatError(error) {
18
116
  if (error instanceof _chunkXAOI3FO6cjs.CanvasApiError) {
@@ -50,47 +148,53 @@ function isNetworkError(error) {
50
148
  return msg.includes("fetch") || msg.includes("enotfound") || msg.includes("econnrefused") || msg.includes("econnreset") || msg.includes("etimedout") || msg.includes("network") || msg.includes("dns") || msg.includes("socket") || error.name === "TypeError";
51
149
  }
52
150
 
53
- // src/tools/health.ts
54
- function healthTools(canvas) {
55
- return [
56
- {
57
- name: "health_check",
58
- description: "Check if the Canvas API is reachable and the token is valid. Returns ok/error status.",
59
- inputSchema: {},
60
- annotations: {
61
- readOnlyHint: true,
62
- openWorldHint: true
63
- },
64
- handler: async () => {
65
- await canvas.users.getProfile();
66
- return { status: "ok", message: "Canvas API is reachable" };
67
- }
68
- }
69
- ];
70
- }
71
-
72
- // src/tools/courses.ts
73
- var _zod = require('zod');
74
- function courseTools(canvas) {
151
+ // src/tools/analytics.ts
152
+ function analyticsTools(canvas) {
75
153
  return [
76
154
  {
77
- name: "list_courses",
78
- description: "List courses for the authenticated user. Optionally filter by enrollment state.",
155
+ name: "search_course_content",
156
+ description: "Search for content within a course. Searches pages, assignments, discussions, and announcements by keyword.",
79
157
  inputSchema: {
80
- enrollment_state: _zod.z.enum(["active", "completed", "all"]).optional().describe("Filter courses by enrollment state")
158
+ course_id: _zod.z.number().describe("The Canvas course ID"),
159
+ search_term: _zod.z.string().describe("The keyword or phrase to search for"),
160
+ content_types: _zod.z.array(_zod.z.enum(_chunkXAOI3FO6cjs.SEARCH_CONTENT_TYPES)).optional().describe(
161
+ "Content types to search. Defaults to all types: pages, discussions, assignments, announcements."
162
+ )
81
163
  },
82
164
  annotations: {
83
165
  readOnlyHint: true,
84
166
  openWorldHint: true
85
167
  },
86
168
  handler: async (params) => {
87
- const enrollment_state = params.enrollment_state;
88
- return canvas.courses.list({ enrollment_state });
169
+ const courseId = params.course_id;
170
+ const searchTerm = params.search_term;
171
+ const types = _nullishCoalesce(params.content_types, () => ( [
172
+ ..._chunkXAOI3FO6cjs.SEARCH_CONTENT_TYPES
173
+ ]));
174
+ if (types.length === 0) return { results: [] };
175
+ const settled = await Promise.allSettled(
176
+ types.map((type) => canvas.analytics.searchContentType(courseId, searchTerm, type))
177
+ );
178
+ const fulfilled = settled.filter(
179
+ (r) => r.status === "fulfilled"
180
+ );
181
+ if (fulfilled.length === 0) {
182
+ const allErrors = settled.map(
183
+ (result, index) => result.status === "rejected" ? `${types[index]}: ${formatError(result.reason)}` : null
184
+ ).filter((errorLine) => errorLine !== null);
185
+ throw new Error(`All content type searches failed:
186
+ ${allErrors.join("\n")}`);
187
+ }
188
+ const results = fulfilled.flatMap((r) => r.value);
189
+ const warnings = settled.map(
190
+ (r, i) => r.status === "rejected" ? `${types[i]} search failed: ${formatError(r.reason)}` : null
191
+ ).filter((w) => w !== null);
192
+ return warnings.length > 0 ? { results, warnings } : { results };
89
193
  }
90
194
  },
91
195
  {
92
- name: "get_course",
93
- description: "Get details for a single course by ID, including term and total student count.",
196
+ name: "get_course_analytics",
197
+ description: "Get course-level activity analytics. Returns daily page view and participation counts.",
94
198
  inputSchema: {
95
199
  course_id: _zod.z.number().describe("The Canvas course ID")
96
200
  },
@@ -100,14 +204,15 @@ function courseTools(canvas) {
100
204
  },
101
205
  handler: async (params) => {
102
206
  const course_id = params.course_id;
103
- return canvas.courses.get(course_id);
207
+ return canvas.analytics.getCourseActivity(course_id);
104
208
  }
105
209
  },
106
210
  {
107
- name: "get_syllabus",
108
- description: "Get the syllabus HTML body for a course.",
211
+ name: "get_student_analytics",
212
+ description: "Get per-student activity analytics for a course. Returns page views, participations, and submission timeline for a specific student.",
109
213
  inputSchema: {
110
- course_id: _zod.z.number().describe("The Canvas course ID")
214
+ course_id: _zod.z.number().describe("The Canvas course ID"),
215
+ student_id: _zod.z.number().describe("The Canvas user ID of the student")
111
216
  },
112
217
  annotations: {
113
218
  readOnlyHint: true,
@@ -115,49 +220,23 @@ function courseTools(canvas) {
115
220
  },
116
221
  handler: async (params) => {
117
222
  const course_id = params.course_id;
118
- const syllabus_body = await canvas.courses.getSyllabus(course_id);
119
- return { course_id, syllabus_body };
120
- }
121
- },
122
- {
123
- name: "create_course",
124
- description: "Create a new course in a Canvas account. Returns the created course object.",
125
- inputSchema: {
126
- account_id: _zod.z.number().int().positive().describe("The Canvas account ID to create the course in"),
127
- name: _zod.z.string().describe("The name of the course"),
128
- course_code: _zod.z.string().optional().describe("The course code (e.g. CS101)"),
129
- start_at: _zod.z.string().datetime().optional().describe("Course start date in ISO 8601 format (e.g. 2026-01-15T00:00:00Z)"),
130
- end_at: _zod.z.string().datetime().optional().describe("Course end date in ISO 8601 format (e.g. 2026-05-15T00:00:00Z)")
131
- },
132
- annotations: {
133
- destructiveHint: true,
134
- openWorldHint: true
135
- },
136
- handler: async (params) => {
137
- const { account_id, ...courseFields } = params;
138
- return canvas.courses.create({ account_id, ...courseFields });
223
+ const student_id = params.student_id;
224
+ return canvas.analytics.getStudentActivity(course_id, student_id);
139
225
  }
140
226
  },
141
227
  {
142
- name: "update_course",
143
- description: "Update an existing course. Only provided fields are changed; omitted fields are left as-is.",
228
+ name: "get_course_activity_stream",
229
+ description: "Get a summary of recent activity in a course. Returns counts of recent events grouped by type (submissions, discussions, announcements, etc.).",
144
230
  inputSchema: {
145
- course_id: _zod.z.number().int().positive().describe("The Canvas course ID to update"),
146
- name: _zod.z.string().optional().describe("New course name"),
147
- course_code: _zod.z.string().optional().describe("New course code"),
148
- start_at: _zod.z.string().datetime().optional().describe("New start date in ISO 8601 format (e.g. 2026-01-15T00:00:00Z)"),
149
- end_at: _zod.z.string().datetime().optional().describe("New end date in ISO 8601 format (e.g. 2026-05-15T00:00:00Z)"),
150
- default_view: _zod.z.enum(["feed", "wiki", "modules", "assignments", "syllabus"]).optional().describe("Default course home page view"),
151
- syllabus_body: _zod.z.string().optional().describe("HTML body for the course syllabus")
231
+ course_id: _zod.z.number().describe("The Canvas course ID")
152
232
  },
153
233
  annotations: {
154
- destructiveHint: true,
155
- idempotentHint: true,
234
+ readOnlyHint: true,
156
235
  openWorldHint: true
157
236
  },
158
237
  handler: async (params) => {
159
- const { course_id, ...fields } = params;
160
- return canvas.courses.update(course_id, fields);
238
+ const course_id = params.course_id;
239
+ return canvas.analytics.getCourseActivityStream(course_id);
161
240
  }
162
241
  }
163
242
  ];
@@ -281,16 +360,15 @@ function assignmentTools(canvas) {
281
360
  ];
282
361
  }
283
362
 
284
- // src/tools/submissions.ts
363
+ // src/tools/calendar.ts
285
364
 
286
- function submissionTools(canvas) {
365
+ function calendarTools(canvas) {
287
366
  return [
288
367
  {
289
- name: "list_submissions",
290
- description: "List all submissions for an assignment in a course.",
368
+ name: "list_calendar_events",
369
+ description: "List calendar events for a course.",
291
370
  inputSchema: {
292
- course_id: _zod.z.number().describe("The Canvas course ID"),
293
- assignment_id: _zod.z.number().describe("The Canvas assignment ID")
371
+ course_id: _zod.z.number().describe("The Canvas course ID")
294
372
  },
295
373
  annotations: {
296
374
  readOnlyHint: true,
@@ -298,201 +376,152 @@ function submissionTools(canvas) {
298
376
  },
299
377
  handler: async (params) => {
300
378
  const course_id = params.course_id;
301
- const assignment_id = params.assignment_id;
302
- return canvas.submissions.list(course_id, assignment_id);
379
+ return canvas.calendar.list(course_id);
303
380
  }
304
381
  },
305
382
  {
306
- name: "get_submission",
307
- description: "Get a single submission for a specific user on an assignment, including comments.",
383
+ name: "create_calendar_event",
384
+ description: "Create a new calendar event in Canvas.",
308
385
  inputSchema: {
309
- course_id: _zod.z.number().describe("The Canvas course ID"),
310
- assignment_id: _zod.z.number().describe("The Canvas assignment ID"),
311
- user_id: _zod.z.number().describe("The Canvas user ID")
386
+ context_code: _zod.z.string().describe('Canvas context code, e.g. "course_123" for a course event'),
387
+ title: _zod.z.string().describe("Event title"),
388
+ start_at: _zod.z.string().describe('Start time in ISO 8601 format, e.g. "2026-05-01T10:00:00Z"'),
389
+ end_at: _zod.z.string().optional().describe("End time in ISO 8601 format. Omit for all-day events."),
390
+ description: _zod.z.string().optional().describe("Event description (HTML allowed)"),
391
+ location_name: _zod.z.string().optional().describe("Location name")
312
392
  },
313
393
  annotations: {
314
- readOnlyHint: true,
394
+ destructiveHint: true,
315
395
  openWorldHint: true
316
396
  },
317
397
  handler: async (params) => {
318
- const course_id = params.course_id;
319
- const assignment_id = params.assignment_id;
320
- const user_id = params.user_id;
321
- return canvas.submissions.get(course_id, assignment_id, user_id);
398
+ return canvas.calendar.createEvent({
399
+ context_code: params.context_code,
400
+ title: params.title,
401
+ start_at: params.start_at,
402
+ end_at: params.end_at,
403
+ description: params.description,
404
+ location_name: params.location_name
405
+ });
322
406
  }
323
407
  },
324
408
  {
325
- name: "grade_submission",
326
- description: "Post or update a grade for a submission. Requires grading permissions.",
327
- inputSchema: {
328
- course_id: _zod.z.number().describe("The Canvas course ID"),
329
- assignment_id: _zod.z.number().describe("The Canvas assignment ID"),
330
- user_id: _zod.z.number().describe("The Canvas user ID"),
331
- grade: _zod.z.string().describe('The grade to assign (e.g. "95", "A", "pass")')
332
- },
333
- annotations: {
334
- destructiveHint: true,
335
- idempotentHint: true,
336
- openWorldHint: true
337
- },
338
- handler: async (params) => {
339
- const course_id = params.course_id;
340
- const assignment_id = params.assignment_id;
341
- const user_id = params.user_id;
342
- const grade = params.grade;
343
- return canvas.submissions.grade(course_id, assignment_id, user_id, grade);
344
- }
345
- },
346
- {
347
- name: "comment_on_submission",
348
- description: "Add a text comment to a submission.",
409
+ name: "update_calendar_event",
410
+ description: "Update an existing calendar event. Only provided fields are changed.",
349
411
  inputSchema: {
350
- course_id: _zod.z.number().describe("The Canvas course ID"),
351
- assignment_id: _zod.z.number().describe("The Canvas assignment ID"),
352
- user_id: _zod.z.number().describe("The Canvas user ID"),
353
- comment: _zod.z.string().describe("The comment text to add")
412
+ event_id: _zod.z.number().describe("The Canvas calendar event ID"),
413
+ title: _zod.z.string().optional().describe("New event title"),
414
+ start_at: _zod.z.string().optional().describe("New start time in ISO 8601 format"),
415
+ end_at: _zod.z.string().optional().describe("New end time in ISO 8601 format"),
416
+ description: _zod.z.string().optional().describe("New description (HTML allowed)"),
417
+ location_name: _zod.z.string().optional().describe("New location name")
354
418
  },
355
419
  annotations: {
356
420
  destructiveHint: true,
357
421
  openWorldHint: true
358
422
  },
359
423
  handler: async (params) => {
360
- const course_id = params.course_id;
361
- const assignment_id = params.assignment_id;
362
- const user_id = params.user_id;
363
- const comment = params.comment;
364
- return canvas.submissions.comment(course_id, assignment_id, user_id, comment);
424
+ const event_id = params.event_id;
425
+ return canvas.calendar.updateEvent(event_id, {
426
+ title: params.title,
427
+ start_at: params.start_at,
428
+ end_at: params.end_at,
429
+ description: params.description,
430
+ location_name: params.location_name
431
+ });
365
432
  }
366
433
  }
367
434
  ];
368
435
  }
369
436
 
370
- // src/tools/rubrics.ts
437
+ // src/tools/conversations.ts
371
438
 
372
- function rubricTools(canvas) {
439
+ function conversationTools(canvas) {
373
440
  return [
374
441
  {
375
- name: "list_rubrics",
376
- description: "List all rubrics in a course.",
377
- inputSchema: {
378
- course_id: _zod.z.number().describe("The Canvas course ID")
379
- },
442
+ name: "list_conversations",
443
+ description: "List conversations (inbox messages) for the authenticated user.",
444
+ inputSchema: {},
380
445
  annotations: {
381
446
  readOnlyHint: true,
382
447
  openWorldHint: true
383
448
  },
384
- handler: async (params) => {
385
- const course_id = params.course_id;
386
- return canvas.rubrics.list(course_id);
449
+ handler: async () => {
450
+ return canvas.conversations.list();
387
451
  }
388
452
  },
389
453
  {
390
- name: "get_rubric",
391
- description: "Get details for a single rubric by ID, including criteria.",
454
+ name: "get_conversation",
455
+ description: "Get a single conversation with its full message thread.",
392
456
  inputSchema: {
393
- course_id: _zod.z.number().describe("The Canvas course ID"),
394
- rubric_id: _zod.z.number().describe("The Canvas rubric ID")
457
+ conversation_id: _zod.z.number().describe("The conversation ID")
395
458
  },
396
459
  annotations: {
397
460
  readOnlyHint: true,
398
461
  openWorldHint: true
399
462
  },
400
463
  handler: async (params) => {
401
- const course_id = params.course_id;
402
- const rubric_id = params.rubric_id;
403
- return canvas.rubrics.get(course_id, rubric_id);
464
+ return canvas.conversations.get(params.conversation_id);
404
465
  }
405
466
  },
406
467
  {
407
- name: "get_rubric_assessment",
408
- description: "Get the rubric assessment for a specific student submission on an assignment.",
409
- inputSchema: {
410
- course_id: _zod.z.number().describe("The Canvas course ID"),
411
- assignment_id: _zod.z.number().describe("The Canvas assignment ID"),
412
- user_id: _zod.z.number().describe("The Canvas user ID")
413
- },
468
+ name: "get_conversation_unread_count",
469
+ description: "Get the number of unread conversations for the authenticated user.",
470
+ inputSchema: {},
414
471
  annotations: {
415
472
  readOnlyHint: true,
416
473
  openWorldHint: true
417
474
  },
418
- handler: async (params) => {
419
- const course_id = params.course_id;
420
- const assignment_id = params.assignment_id;
421
- const user_id = params.user_id;
422
- return canvas.rubrics.getAssessment(course_id, assignment_id, user_id);
475
+ handler: async () => {
476
+ return canvas.conversations.getUnreadCount();
423
477
  }
424
478
  },
425
479
  {
426
- name: "submit_rubric_assessment",
427
- description: "Submit a rubric assessment with scores and comments for each criterion.",
480
+ name: "send_conversation",
481
+ description: "Send a new conversation message to one or more recipients.",
428
482
  inputSchema: {
429
- course_id: _zod.z.number().describe("The Canvas course ID"),
430
- association_id: _zod.z.number().describe("The rubric association ID"),
431
- data: _zod.z.array(
432
- _zod.z.object({
433
- criterion_id: _zod.z.string().describe("The rubric criterion ID"),
434
- points: _zod.z.number().describe("Points awarded for this criterion"),
435
- comments: _zod.z.string().describe("Feedback comment for this criterion")
436
- })
437
- ).describe("Array of criterion assessments")
483
+ recipients: _zod.z.array(_zod.z.string()).describe("Array of recipient user IDs (as strings)"),
484
+ subject: _zod.z.string().describe("The conversation subject line"),
485
+ body: _zod.z.string().describe("The message body")
438
486
  },
439
487
  annotations: {
440
488
  destructiveHint: true,
441
- idempotentHint: true,
442
489
  openWorldHint: true
443
490
  },
444
491
  handler: async (params) => {
445
- const course_id = params.course_id;
446
- const association_id = params.association_id;
447
- const data = params.data;
448
- return canvas.rubrics.submitAssessment(course_id, association_id, data);
492
+ const recipients = params.recipients;
493
+ const subject = params.subject;
494
+ const body = params.body;
495
+ return canvas.conversations.send(recipients, subject, body);
449
496
  }
450
497
  }
451
498
  ];
452
499
  }
453
500
 
454
- // src/tools/quizzes.ts
501
+ // src/tools/courses.ts
455
502
 
456
- function quizTools(canvas) {
503
+ function courseTools(canvas) {
457
504
  return [
458
505
  {
459
- name: "list_quizzes",
460
- description: "List all quizzes in a course.",
461
- inputSchema: {
462
- course_id: _zod.z.number().describe("The Canvas course ID")
463
- },
464
- annotations: {
465
- readOnlyHint: true,
466
- openWorldHint: true
467
- },
468
- handler: async (params) => {
469
- const course_id = params.course_id;
470
- return canvas.quizzes.list(course_id);
471
- }
472
- },
473
- {
474
- name: "get_quiz",
475
- description: "Get details for a single quiz by ID.",
506
+ name: "list_courses",
507
+ description: "List courses for the authenticated user. Optionally filter by enrollment state.",
476
508
  inputSchema: {
477
- course_id: _zod.z.number().describe("The Canvas course ID"),
478
- quiz_id: _zod.z.number().describe("The Canvas quiz ID")
509
+ enrollment_state: _zod.z.enum(["active", "completed", "all"]).optional().describe("Filter courses by enrollment state")
479
510
  },
480
511
  annotations: {
481
512
  readOnlyHint: true,
482
513
  openWorldHint: true
483
514
  },
484
515
  handler: async (params) => {
485
- const course_id = params.course_id;
486
- const quiz_id = params.quiz_id;
487
- return canvas.quizzes.get(course_id, quiz_id);
516
+ const enrollment_state = params.enrollment_state;
517
+ return canvas.courses.list({ enrollment_state });
488
518
  }
489
519
  },
490
520
  {
491
- name: "list_quiz_submissions",
492
- description: "List all submissions for a quiz.",
521
+ name: "get_course",
522
+ description: "Get details for a single course by ID, including term and total student count.",
493
523
  inputSchema: {
494
- course_id: _zod.z.number().describe("The Canvas course ID"),
495
- quiz_id: _zod.z.number().describe("The Canvas quiz ID")
524
+ course_id: _zod.z.number().describe("The Canvas course ID")
496
525
  },
497
526
  annotations: {
498
527
  readOnlyHint: true,
@@ -500,16 +529,14 @@ function quizTools(canvas) {
500
529
  },
501
530
  handler: async (params) => {
502
531
  const course_id = params.course_id;
503
- const quiz_id = params.quiz_id;
504
- return canvas.quizzes.listSubmissions(course_id, quiz_id);
532
+ return canvas.courses.get(course_id);
505
533
  }
506
534
  },
507
535
  {
508
- name: "list_quiz_questions",
509
- description: "List all questions in a quiz.",
536
+ name: "get_syllabus",
537
+ description: "Get the syllabus HTML body for a course.",
510
538
  inputSchema: {
511
- course_id: _zod.z.number().describe("The Canvas course ID"),
512
- quiz_id: _zod.z.number().describe("The Canvas quiz ID")
539
+ course_id: _zod.z.number().describe("The Canvas course ID")
513
540
  },
514
541
  annotations: {
515
542
  readOnlyHint: true,
@@ -517,36 +544,40 @@ function quizTools(canvas) {
517
544
  },
518
545
  handler: async (params) => {
519
546
  const course_id = params.course_id;
520
- const quiz_id = params.quiz_id;
521
- return canvas.quizzes.listQuestions(course_id, quiz_id);
547
+ const syllabus_body = await canvas.courses.getSyllabus(course_id);
548
+ return { course_id, syllabus_body };
522
549
  }
523
550
  },
524
551
  {
525
- name: "get_quiz_submission_answers",
526
- description: "Get a student's answers for a quiz submission.",
552
+ name: "create_course",
553
+ description: "Create a new course in a Canvas account. Returns the created course object.",
527
554
  inputSchema: {
528
- quiz_submission_id: _zod.z.number().describe("The Canvas quiz submission ID")
555
+ account_id: _zod.z.number().int().positive().describe("The Canvas account ID to create the course in"),
556
+ name: _zod.z.string().describe("The name of the course"),
557
+ course_code: _zod.z.string().optional().describe("The course code (e.g. CS101)"),
558
+ start_at: _zod.z.string().datetime().optional().describe("Course start date in ISO 8601 format (e.g. 2026-01-15T00:00:00Z)"),
559
+ end_at: _zod.z.string().datetime().optional().describe("Course end date in ISO 8601 format (e.g. 2026-05-15T00:00:00Z)")
529
560
  },
530
561
  annotations: {
531
- readOnlyHint: true,
562
+ destructiveHint: true,
532
563
  openWorldHint: true
533
564
  },
534
565
  handler: async (params) => {
535
- const quiz_submission_id = params.quiz_submission_id;
536
- return canvas.quizzes.getSubmissionAnswers(quiz_submission_id);
566
+ const { account_id, ...courseFields } = params;
567
+ return canvas.courses.create({ account_id, ...courseFields });
537
568
  }
538
569
  },
539
570
  {
540
- name: "score_quiz_question",
541
- description: "Score a specific question in a quiz submission. Specify attempt to score a particular attempt (omit for latest). Requires grading permissions.",
571
+ name: "update_course",
572
+ description: "Update an existing course. Only provided fields are changed; omitted fields are left as-is.",
542
573
  inputSchema: {
543
- course_id: _zod.z.number().describe("The Canvas course ID"),
544
- quiz_id: _zod.z.number().describe("The Canvas quiz ID"),
545
- submission_id: _zod.z.number().describe("The Canvas quiz submission ID"),
546
- question_id: _zod.z.number().describe("The Canvas quiz question ID"),
547
- score: _zod.z.number().describe("The score to assign"),
548
- comment: _zod.z.string().optional().describe("Optional feedback comment"),
549
- attempt: _zod.z.number().optional().describe("Quiz attempt number to score (omit for latest)")
574
+ course_id: _zod.z.number().int().positive().describe("The Canvas course ID to update"),
575
+ name: _zod.z.string().optional().describe("New course name"),
576
+ course_code: _zod.z.string().optional().describe("New course code"),
577
+ start_at: _zod.z.string().datetime().optional().describe("New start date in ISO 8601 format (e.g. 2026-01-15T00:00:00Z)"),
578
+ end_at: _zod.z.string().datetime().optional().describe("New end date in ISO 8601 format (e.g. 2026-05-15T00:00:00Z)"),
579
+ default_view: _zod.z.enum(["feed", "wiki", "modules", "assignments", "syllabus"]).optional().describe("Default course home page view"),
580
+ syllabus_body: _zod.z.string().optional().describe("HTML body for the course syllabus")
550
581
  },
551
582
  annotations: {
552
583
  destructiveHint: true,
@@ -554,127 +585,74 @@ function quizTools(canvas) {
554
585
  openWorldHint: true
555
586
  },
556
587
  handler: async (params) => {
557
- const course_id = params.course_id;
558
- const quiz_id = params.quiz_id;
559
- const submission_id = params.submission_id;
560
- const question_id = params.question_id;
561
- const score = params.score;
562
- const comment = params.comment;
563
- const attempt = params.attempt;
564
- await canvas.quizzes.scoreQuestion(
565
- course_id,
566
- quiz_id,
567
- submission_id,
568
- question_id,
569
- score,
570
- comment,
571
- attempt
572
- );
573
- return { success: true };
588
+ const { course_id, ...fields } = params;
589
+ return canvas.courses.update(course_id, fields);
574
590
  }
575
591
  }
576
592
  ];
577
593
  }
578
594
 
579
- // src/tools/files.ts
580
-
581
- function fileTools(canvas) {
595
+ // src/tools/dashboard.ts
596
+ function dashboardTools(canvas) {
582
597
  return [
583
598
  {
584
- name: "list_files",
585
- description: "List all files in a course.",
586
- inputSchema: {
587
- course_id: _zod.z.number().describe("The Canvas course ID")
588
- },
599
+ name: "get_dashboard_cards",
600
+ description: "Get the current user's dashboard course cards with position, color, and image.",
601
+ inputSchema: {},
589
602
  annotations: {
590
603
  readOnlyHint: true,
591
604
  openWorldHint: true
592
605
  },
593
- handler: async (params) => {
594
- const course_id = params.course_id;
595
- return canvas.files.list(course_id);
606
+ handler: async () => {
607
+ return canvas.dashboard.getDashboardCards();
596
608
  }
597
609
  },
598
610
  {
599
- name: "list_folders",
600
- description: "List all folders in a course.",
601
- inputSchema: {
602
- course_id: _zod.z.number().describe("The Canvas course ID")
603
- },
611
+ name: "get_todo_items",
612
+ description: "Get the current user's to-do items, including upcoming assignments and grading tasks.",
613
+ inputSchema: {},
604
614
  annotations: {
605
615
  readOnlyHint: true,
606
616
  openWorldHint: true
607
617
  },
608
- handler: async (params) => {
609
- const course_id = params.course_id;
610
- return canvas.files.listFolders(course_id);
618
+ handler: async () => {
619
+ return canvas.dashboard.getTodoItems();
611
620
  }
612
621
  },
613
622
  {
614
- name: "get_file",
615
- description: "Get metadata for a single file by ID, including download URL.",
616
- inputSchema: {
617
- course_id: _zod.z.number().describe("The Canvas course ID"),
618
- file_id: _zod.z.number().describe("The Canvas file ID")
619
- },
623
+ name: "get_upcoming_events",
624
+ description: "Get the current user's upcoming calendar events and assignments.",
625
+ inputSchema: {},
620
626
  annotations: {
621
627
  readOnlyHint: true,
622
628
  openWorldHint: true
623
629
  },
624
- handler: async (params) => {
625
- const course_id = params.course_id;
626
- const file_id = params.file_id;
627
- return canvas.files.get(course_id, file_id);
628
- }
629
- },
630
- {
631
- name: "upload_file",
632
- description: "Upload a file to a course. Content must be base64-encoded. Canvas performs a multi-step upload internally.",
633
- inputSchema: {
634
- course_id: _zod.z.number().describe("The Canvas course ID"),
635
- name: _zod.z.string().describe("File name including extension"),
636
- content: _zod.z.string().describe("Base64-encoded file content"),
637
- content_type: _zod.z.string().describe('MIME type, e.g. "application/pdf" or "image/png"'),
638
- parent_folder_path: _zod.z.string().optional().describe('Destination folder path within the course, e.g. "subfolder/nested"')
639
- },
640
- annotations: {
641
- destructiveHint: true,
642
- openWorldHint: true
643
- },
644
- handler: async (params) => {
645
- const course_id = params.course_id;
646
- const name = params.name;
647
- const content = params.content;
648
- const content_type = params.content_type;
649
- const parent_folder_path = params.parent_folder_path;
650
- return canvas.files.upload(course_id, name, content, content_type, parent_folder_path);
630
+ handler: async () => {
631
+ return canvas.dashboard.getUpcomingEvents();
651
632
  }
652
633
  },
653
634
  {
654
- name: "delete_file",
655
- description: "Delete a file by ID. This action is permanent.",
656
- inputSchema: {
657
- file_id: _zod.z.number().describe("The Canvas file ID")
658
- },
635
+ name: "get_missing_submissions",
636
+ description: "Get assignments with missing submissions for the current user.",
637
+ inputSchema: {},
659
638
  annotations: {
660
- destructiveHint: true,
639
+ readOnlyHint: true,
661
640
  openWorldHint: true
662
641
  },
663
- handler: async (params) => {
664
- const file_id = params.file_id;
665
- return canvas.files.delete(file_id);
642
+ handler: async () => {
643
+ return canvas.dashboard.getMissingSubmissions();
666
644
  }
667
645
  }
668
646
  ];
669
647
  }
670
648
 
671
- // src/tools/users.ts
649
+ // src/tools/discussions.ts
672
650
 
673
- function userTools(canvas) {
651
+ function discussionTools(canvas) {
674
652
  return [
675
653
  {
676
- name: "list_students",
677
- description: "List all students enrolled in a course.",
654
+ name: "list_discussions",
655
+ description: "List all discussion topics in a course.",
678
656
  inputSchema: {
679
657
  course_id: _zod.z.number().describe("The Canvas course ID")
680
658
  },
@@ -684,111 +662,132 @@ function userTools(canvas) {
684
662
  },
685
663
  handler: async (params) => {
686
664
  const course_id = params.course_id;
687
- return canvas.users.listStudents(course_id);
665
+ return canvas.discussions.list(course_id);
688
666
  }
689
667
  },
690
668
  {
691
- name: "get_user",
692
- description: "Get details for a single user by ID.",
669
+ name: "get_discussion",
670
+ description: "Get details for a single discussion topic by ID.",
693
671
  inputSchema: {
694
- user_id: _zod.z.number().describe("The Canvas user ID")
672
+ course_id: _zod.z.number().describe("The Canvas course ID"),
673
+ topic_id: _zod.z.number().describe("The Canvas discussion topic ID")
695
674
  },
696
675
  annotations: {
697
676
  readOnlyHint: true,
698
677
  openWorldHint: true
699
678
  },
700
679
  handler: async (params) => {
701
- const user_id = params.user_id;
702
- return canvas.users.get(user_id);
680
+ const course_id = params.course_id;
681
+ const topic_id = params.topic_id;
682
+ return canvas.discussions.get(course_id, topic_id);
703
683
  }
704
684
  },
705
685
  {
706
- name: "get_profile",
707
- description: "Get the profile of the currently authenticated user.",
708
- inputSchema: {},
686
+ name: "list_announcements",
687
+ description: "List all announcements in a course.",
688
+ inputSchema: {
689
+ course_id: _zod.z.number().describe("The Canvas course ID")
690
+ },
709
691
  annotations: {
710
692
  readOnlyHint: true,
711
693
  openWorldHint: true
712
694
  },
713
- handler: async () => {
714
- return canvas.users.getProfile();
695
+ handler: async (params) => {
696
+ const course_id = params.course_id;
697
+ return canvas.discussions.listAnnouncements(course_id);
715
698
  }
716
699
  },
717
700
  {
718
- name: "search_users",
719
- description: "Search for users in a Canvas account by name, login, or email.",
701
+ name: "post_discussion_entry",
702
+ description: "Post a new entry (reply) to a discussion topic.",
720
703
  inputSchema: {
721
- account_id: _zod.z.number().describe("The Canvas account ID"),
722
- search_term: _zod.z.string().describe("The search term (name, login, or email)"),
723
- sort: _zod.z.enum(["username", "email", "sis_id", "last_login"]).optional().describe("Sort field"),
724
- order: _zod.z.enum(["asc", "desc"]).optional().describe("Sort order")
704
+ course_id: _zod.z.number().describe("The Canvas course ID"),
705
+ topic_id: _zod.z.number().describe("The Canvas discussion topic ID"),
706
+ message: _zod.z.string().describe("The message body (supports HTML)")
725
707
  },
726
708
  annotations: {
727
- readOnlyHint: true,
709
+ destructiveHint: true,
728
710
  openWorldHint: true
729
711
  },
730
712
  handler: async (params) => {
731
- return canvas.users.searchUsers(
732
- params.account_id,
733
- params.search_term,
734
- params.sort,
735
- params.order
736
- );
713
+ const course_id = params.course_id;
714
+ const topic_id = params.topic_id;
715
+ const message = params.message;
716
+ return canvas.discussions.postEntry(course_id, topic_id, message);
737
717
  }
738
718
  },
739
719
  {
740
- name: "list_course_users",
741
- description: "List all users in a course, optionally filtered by enrollment type.",
720
+ name: "create_discussion",
721
+ description: "Create a new discussion topic in a course.",
742
722
  inputSchema: {
743
723
  course_id: _zod.z.number().describe("The Canvas course ID"),
744
- enrollment_type: _zod.z.enum(["student", "teacher", "ta", "observer", "designer"]).optional().describe("Filter by enrollment type")
724
+ title: _zod.z.string().min(1).describe("Title of the discussion topic"),
725
+ message: _zod.z.string().optional().describe("Body text of the discussion (supports HTML)"),
726
+ discussion_type: _zod.z.enum(["side_comment", "threaded"]).optional().describe("Discussion type: side_comment (flat) or threaded"),
727
+ published: _zod.z.boolean().optional().describe("Whether the topic is published"),
728
+ require_initial_post: _zod.z.boolean().optional().describe("Require students to post before seeing replies")
745
729
  },
746
730
  annotations: {
747
- readOnlyHint: true,
731
+ destructiveHint: true,
748
732
  openWorldHint: true
749
733
  },
750
734
  handler: async (params) => {
751
- return canvas.users.listCourseUsers(
752
- params.course_id,
753
- params.enrollment_type
754
- );
735
+ const course_id = params.course_id;
736
+ return canvas.discussions.create(course_id, {
737
+ title: params.title,
738
+ message: params.message,
739
+ discussion_type: params.discussion_type,
740
+ published: params.published,
741
+ require_initial_post: params.require_initial_post
742
+ });
755
743
  }
756
- }
757
- ];
758
- }
759
-
760
- // src/tools/groups.ts
761
-
762
- function groupTools(canvas) {
763
- return [
744
+ },
764
745
  {
765
- name: "list_groups",
766
- description: "List all groups in a course.",
746
+ name: "update_discussion",
747
+ description: "Update an existing discussion topic.",
767
748
  inputSchema: {
768
- course_id: _zod.z.number().describe("The Canvas course ID")
749
+ course_id: _zod.z.number().describe("The Canvas course ID"),
750
+ topic_id: _zod.z.number().describe("The Canvas discussion topic ID"),
751
+ title: _zod.z.string().optional().describe("New title for the discussion topic"),
752
+ message: _zod.z.string().optional().describe("New body text (supports HTML)"),
753
+ published: _zod.z.boolean().optional().describe("Publish or unpublish the topic"),
754
+ require_initial_post: _zod.z.boolean().optional().describe("Require students to post before seeing replies")
769
755
  },
770
756
  annotations: {
771
- readOnlyHint: true,
757
+ destructiveHint: true,
772
758
  openWorldHint: true
773
759
  },
774
760
  handler: async (params) => {
775
761
  const course_id = params.course_id;
776
- return canvas.groups.list(course_id);
762
+ const topic_id = params.topic_id;
763
+ const updateParams = {
764
+ title: params.title,
765
+ message: params.message,
766
+ published: params.published,
767
+ require_initial_post: params.require_initial_post
768
+ };
769
+ if (Object.values(updateParams).every((v) => v === void 0)) {
770
+ throw new Error("At least one field must be provided to update a discussion topic");
771
+ }
772
+ return canvas.discussions.update(course_id, topic_id, updateParams);
777
773
  }
778
774
  },
779
775
  {
780
- name: "list_group_members",
781
- description: "List all members of a group.",
776
+ name: "delete_discussion",
777
+ description: "Delete a discussion topic from a course.",
782
778
  inputSchema: {
783
- group_id: _zod.z.number().describe("The Canvas group ID")
779
+ course_id: _zod.z.number().describe("The Canvas course ID"),
780
+ topic_id: _zod.z.number().describe("The Canvas discussion topic ID to delete")
784
781
  },
785
782
  annotations: {
786
- readOnlyHint: true,
783
+ destructiveHint: true,
787
784
  openWorldHint: true
788
785
  },
789
786
  handler: async (params) => {
790
- const group_id = params.group_id;
791
- return canvas.groups.listMembers(group_id);
787
+ const course_id = params.course_id;
788
+ const topic_id = params.topic_id;
789
+ await canvas.discussions.delete(course_id, topic_id);
790
+ return { deleted: true, topic_id };
792
791
  }
793
792
  }
794
793
  ];
@@ -861,13 +860,13 @@ function enrollmentTools(canvas) {
861
860
  ];
862
861
  }
863
862
 
864
- // src/tools/discussions.ts
863
+ // src/tools/files.ts
865
864
 
866
- function discussionTools(canvas) {
865
+ function fileTools(canvas) {
867
866
  return [
868
867
  {
869
- name: "list_discussions",
870
- description: "List all discussion topics in a course.",
868
+ name: "list_files",
869
+ description: "List all files in a course.",
871
870
  inputSchema: {
872
871
  course_id: _zod.z.number().describe("The Canvas course ID")
873
872
  },
@@ -877,15 +876,14 @@ function discussionTools(canvas) {
877
876
  },
878
877
  handler: async (params) => {
879
878
  const course_id = params.course_id;
880
- return canvas.discussions.list(course_id);
879
+ return canvas.files.list(course_id);
881
880
  }
882
881
  },
883
882
  {
884
- name: "get_discussion",
885
- description: "Get details for a single discussion topic by ID.",
883
+ name: "list_folders",
884
+ description: "List all folders in a course.",
886
885
  inputSchema: {
887
- course_id: _zod.z.number().describe("The Canvas course ID"),
888
- topic_id: _zod.z.number().describe("The Canvas discussion topic ID")
886
+ course_id: _zod.z.number().describe("The Canvas course ID")
889
887
  },
890
888
  annotations: {
891
889
  readOnlyHint: true,
@@ -893,15 +891,15 @@ function discussionTools(canvas) {
893
891
  },
894
892
  handler: async (params) => {
895
893
  const course_id = params.course_id;
896
- const topic_id = params.topic_id;
897
- return canvas.discussions.get(course_id, topic_id);
894
+ return canvas.files.listFolders(course_id);
898
895
  }
899
896
  },
900
897
  {
901
- name: "list_announcements",
902
- description: "List all announcements in a course.",
898
+ name: "get_file",
899
+ description: "Get metadata for a single file by ID, including download URL.",
903
900
  inputSchema: {
904
- course_id: _zod.z.number().describe("The Canvas course ID")
901
+ course_id: _zod.z.number().describe("The Canvas course ID"),
902
+ file_id: _zod.z.number().describe("The Canvas file ID")
905
903
  },
906
904
  annotations: {
907
905
  readOnlyHint: true,
@@ -909,16 +907,19 @@ function discussionTools(canvas) {
909
907
  },
910
908
  handler: async (params) => {
911
909
  const course_id = params.course_id;
912
- return canvas.discussions.listAnnouncements(course_id);
910
+ const file_id = params.file_id;
911
+ return canvas.files.get(course_id, file_id);
913
912
  }
914
913
  },
915
914
  {
916
- name: "post_discussion_entry",
917
- description: "Post a new entry (reply) to a discussion topic.",
915
+ name: "upload_file",
916
+ description: "Upload a file to a course. Content must be base64-encoded. Canvas performs a multi-step upload internally.",
918
917
  inputSchema: {
919
918
  course_id: _zod.z.number().describe("The Canvas course ID"),
920
- topic_id: _zod.z.number().describe("The Canvas discussion topic ID"),
921
- message: _zod.z.string().describe("The message body (supports HTML)")
919
+ name: _zod.z.string().describe("File name including extension"),
920
+ content: _zod.z.string().describe("Base64-encoded file content"),
921
+ content_type: _zod.z.string().describe('MIME type, e.g. "application/pdf" or "image/png"'),
922
+ parent_folder_path: _zod.z.string().optional().describe('Destination folder path within the course, e.g. "subfolder/nested"')
922
923
  },
923
924
  annotations: {
924
925
  destructiveHint: true,
@@ -926,83 +927,82 @@ function discussionTools(canvas) {
926
927
  },
927
928
  handler: async (params) => {
928
929
  const course_id = params.course_id;
929
- const topic_id = params.topic_id;
930
- const message = params.message;
931
- return canvas.discussions.postEntry(course_id, topic_id, message);
930
+ const name = params.name;
931
+ const content = params.content;
932
+ const content_type = params.content_type;
933
+ const parent_folder_path = params.parent_folder_path;
934
+ return canvas.files.upload(course_id, name, content, content_type, parent_folder_path);
932
935
  }
933
936
  },
934
937
  {
935
- name: "create_discussion",
936
- description: "Create a new discussion topic in a course.",
938
+ name: "delete_file",
939
+ description: "Delete a file by ID. This action is permanent.",
937
940
  inputSchema: {
938
- course_id: _zod.z.number().describe("The Canvas course ID"),
939
- title: _zod.z.string().min(1).describe("Title of the discussion topic"),
940
- message: _zod.z.string().optional().describe("Body text of the discussion (supports HTML)"),
941
- discussion_type: _zod.z.enum(["side_comment", "threaded"]).optional().describe("Discussion type: side_comment (flat) or threaded"),
942
- published: _zod.z.boolean().optional().describe("Whether the topic is published"),
943
- require_initial_post: _zod.z.boolean().optional().describe("Require students to post before seeing replies")
941
+ file_id: _zod.z.number().describe("The Canvas file ID")
944
942
  },
945
943
  annotations: {
946
944
  destructiveHint: true,
947
945
  openWorldHint: true
948
946
  },
949
947
  handler: async (params) => {
950
- const course_id = params.course_id;
951
- return canvas.discussions.create(course_id, {
952
- title: params.title,
953
- message: params.message,
954
- discussion_type: params.discussion_type,
955
- published: params.published,
956
- require_initial_post: params.require_initial_post
957
- });
948
+ const file_id = params.file_id;
949
+ return canvas.files.delete(file_id);
958
950
  }
959
- },
960
- {
961
- name: "update_discussion",
962
- description: "Update an existing discussion topic.",
963
- inputSchema: {
964
- course_id: _zod.z.number().describe("The Canvas course ID"),
965
- topic_id: _zod.z.number().describe("The Canvas discussion topic ID"),
966
- title: _zod.z.string().optional().describe("New title for the discussion topic"),
967
- message: _zod.z.string().optional().describe("New body text (supports HTML)"),
968
- published: _zod.z.boolean().optional().describe("Publish or unpublish the topic"),
969
- require_initial_post: _zod.z.boolean().optional().describe("Require students to post before seeing replies")
951
+ }
952
+ ];
953
+ }
954
+
955
+ // src/tools/groups.ts
956
+
957
+ function groupTools(canvas) {
958
+ return [
959
+ {
960
+ name: "list_groups",
961
+ description: "List all groups in a course.",
962
+ inputSchema: {
963
+ course_id: _zod.z.number().describe("The Canvas course ID")
970
964
  },
971
965
  annotations: {
972
- destructiveHint: true,
966
+ readOnlyHint: true,
973
967
  openWorldHint: true
974
968
  },
975
969
  handler: async (params) => {
976
970
  const course_id = params.course_id;
977
- const topic_id = params.topic_id;
978
- const updateParams = {
979
- title: params.title,
980
- message: params.message,
981
- published: params.published,
982
- require_initial_post: params.require_initial_post
983
- };
984
- if (Object.values(updateParams).every((v) => v === void 0)) {
985
- throw new Error("At least one field must be provided to update a discussion topic");
986
- }
987
- return canvas.discussions.update(course_id, topic_id, updateParams);
971
+ return canvas.groups.list(course_id);
988
972
  }
989
973
  },
990
974
  {
991
- name: "delete_discussion",
992
- description: "Delete a discussion topic from a course.",
975
+ name: "list_group_members",
976
+ description: "List all members of a group.",
993
977
  inputSchema: {
994
- course_id: _zod.z.number().describe("The Canvas course ID"),
995
- topic_id: _zod.z.number().describe("The Canvas discussion topic ID to delete")
978
+ group_id: _zod.z.number().describe("The Canvas group ID")
996
979
  },
997
980
  annotations: {
998
- destructiveHint: true,
981
+ readOnlyHint: true,
999
982
  openWorldHint: true
1000
983
  },
1001
984
  handler: async (params) => {
1002
- const course_id = params.course_id;
1003
- const topic_id = params.topic_id;
1004
- await canvas.discussions.delete(course_id, topic_id);
1005
- return { deleted: true, topic_id };
985
+ const group_id = params.group_id;
986
+ return canvas.groups.listMembers(group_id);
987
+ }
988
+ }
989
+ ];
990
+ }
991
+
992
+ // src/tools/health.ts
993
+ function healthTools(canvas) {
994
+ return [
995
+ {
996
+ name: "health_check",
997
+ description: "Check if the Canvas API is reachable and the token is valid. Returns ok/error status.",
998
+ inputSchema: {},
999
+ annotations: {
1000
+ readOnlyHint: true,
1001
+ openWorldHint: true
1002
+ },
1003
+ handler: async () => {
1004
+ await canvas.users.getProfile();
1005
+ return { status: "ok", message: "Canvas API is reachable" };
1006
1006
  }
1007
1007
  }
1008
1008
  ];
@@ -1151,246 +1151,330 @@ function moduleTools(canvas) {
1151
1151
  ];
1152
1152
  }
1153
1153
 
1154
- // src/tools/pages.ts
1154
+ // src/tools/outcomes.ts
1155
1155
 
1156
- function pageTools(canvas) {
1156
+ function outcomeTools(canvas) {
1157
1157
  return [
1158
1158
  {
1159
- name: "list_pages",
1160
- description: "List all wiki pages in a course.",
1159
+ name: "get_root_outcome_group",
1160
+ description: "Get the root outcome group for an account or course context.",
1161
1161
  inputSchema: {
1162
- course_id: _zod.z.number().describe("The Canvas course ID")
1163
- },
1164
- annotations: {
1165
- readOnlyHint: true,
1166
- openWorldHint: true
1162
+ context_type: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_CONTEXT_TYPES).describe("Whether to read outcomes from an account or course context."),
1163
+ context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context.")
1167
1164
  },
1168
- handler: async (params) => {
1169
- const course_id = params.course_id;
1170
- return canvas.pages.list(course_id);
1171
- }
1165
+ annotations: { readOnlyHint: true, openWorldHint: true },
1166
+ handler: async (params) => canvas.outcomes.getRootOutcomeGroup(
1167
+ params.context_type,
1168
+ params.context_id
1169
+ )
1172
1170
  },
1173
1171
  {
1174
- name: "get_page",
1175
- description: "Get a single wiki page by its URL slug.",
1172
+ name: "list_outcome_groups",
1173
+ description: "List all outcome groups for an account or course context.",
1176
1174
  inputSchema: {
1177
- course_id: _zod.z.number().describe("The Canvas course ID"),
1178
- page_url: _zod.z.string().describe('The page URL slug (e.g. "welcome-page")')
1179
- },
1180
- annotations: {
1181
- readOnlyHint: true,
1182
- openWorldHint: true
1175
+ context_type: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_CONTEXT_TYPES).describe("Whether to read outcome groups from an account or course context."),
1176
+ context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context.")
1183
1177
  },
1184
- handler: async (params) => {
1185
- const course_id = params.course_id;
1186
- const page_url = params.page_url;
1187
- return canvas.pages.get(course_id, page_url);
1188
- }
1178
+ annotations: { readOnlyHint: true, openWorldHint: true },
1179
+ handler: async (params) => canvas.outcomes.listOutcomeGroups(
1180
+ params.context_type,
1181
+ params.context_id
1182
+ )
1189
1183
  },
1190
1184
  {
1191
- name: "create_page",
1192
- description: "Create a new wiki page in a course.",
1185
+ name: "list_outcome_group_links",
1186
+ description: "List all outcome links in an account or course context.",
1193
1187
  inputSchema: {
1194
- course_id: _zod.z.number().describe("The Canvas course ID"),
1195
- title: _zod.z.string().describe("Title of the page"),
1196
- body: _zod.z.string().optional().describe("HTML body content of the page"),
1197
- published: _zod.z.boolean().optional().describe("Whether the page is published"),
1198
- editing_roles: _zod.z.string().optional().describe('Who can edit: "teachers", "students", "members", or "public"')
1199
- },
1200
- annotations: {
1201
- destructiveHint: true,
1202
- openWorldHint: true
1188
+ context_type: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_CONTEXT_TYPES).describe("Whether to read outcome links from an account or course context."),
1189
+ context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
1190
+ outcome_style: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_DETAIL_LEVELS).optional().describe('Outcome detail level. Use "full" to include expanded outcome fields.'),
1191
+ outcome_group_style: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_DETAIL_LEVELS).optional().describe(
1192
+ 'Outcome group detail level. Use "full" to include expanded outcome group fields.'
1193
+ )
1203
1194
  },
1204
- handler: async (params) => {
1205
- const course_id = params.course_id;
1206
- return canvas.pages.create(course_id, {
1207
- title: params.title,
1208
- body: params.body,
1209
- published: params.published,
1210
- editing_roles: params.editing_roles
1211
- });
1212
- }
1195
+ annotations: { readOnlyHint: true, openWorldHint: true },
1196
+ handler: async (params) => canvas.outcomes.listOutcomeGroupLinks(
1197
+ params.context_type,
1198
+ params.context_id,
1199
+ {
1200
+ outcome_style: params.outcome_style,
1201
+ outcome_group_style: params.outcome_group_style
1202
+ }
1203
+ )
1213
1204
  },
1214
1205
  {
1215
- name: "update_page",
1216
- description: "Update an existing wiki page.",
1206
+ name: "get_outcome_group",
1207
+ description: "Get details for a specific outcome group in an account or course context.",
1217
1208
  inputSchema: {
1218
- course_id: _zod.z.number().describe("The Canvas course ID"),
1219
- page_url: _zod.z.string().describe("The page URL slug"),
1220
- title: _zod.z.string().optional().describe("New title for the page"),
1221
- body: _zod.z.string().optional().describe("New HTML body content"),
1222
- published: _zod.z.boolean().optional().describe("Whether the page is published"),
1223
- editing_roles: _zod.z.string().optional().describe('Who can edit: "teachers", "students", "members", or "public"')
1224
- },
1225
- annotations: {
1226
- destructiveHint: true,
1227
- openWorldHint: true
1209
+ context_type: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_CONTEXT_TYPES).describe("The outcome group context type."),
1210
+ context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
1211
+ outcome_group_id: _zod.z.number().describe("The Canvas outcome group ID.")
1228
1212
  },
1229
- handler: async (params) => {
1230
- const course_id = params.course_id;
1231
- const page_url = params.page_url;
1232
- return canvas.pages.update(course_id, page_url, {
1233
- title: params.title,
1234
- body: params.body,
1235
- published: params.published,
1236
- editing_roles: params.editing_roles
1237
- });
1238
- }
1213
+ annotations: { readOnlyHint: true, openWorldHint: true },
1214
+ handler: async (params) => canvas.outcomes.getOutcomeGroup(
1215
+ params.context_type,
1216
+ params.context_id,
1217
+ params.outcome_group_id
1218
+ )
1239
1219
  },
1240
1220
  {
1241
- name: "delete_page",
1242
- description: "Delete a wiki page from a course.",
1221
+ name: "list_outcome_group_outcomes",
1222
+ description: "List the linked outcomes directly under a specific outcome group.",
1243
1223
  inputSchema: {
1244
- course_id: _zod.z.number().describe("The Canvas course ID"),
1245
- page_url: _zod.z.string().describe("The page URL slug to delete")
1246
- },
1247
- annotations: {
1248
- destructiveHint: true,
1249
- openWorldHint: true
1224
+ context_type: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_CONTEXT_TYPES).describe("The outcome group context type."),
1225
+ context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
1226
+ outcome_group_id: _zod.z.number().describe("The Canvas outcome group ID."),
1227
+ outcome_style: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_DETAIL_LEVELS).optional().describe('Outcome detail level. Use "full" to include expanded outcome fields.')
1250
1228
  },
1251
- handler: async (params) => {
1252
- const course_id = params.course_id;
1253
- const page_url = params.page_url;
1254
- await canvas.pages.delete(course_id, page_url);
1255
- return { deleted: true, page_url };
1256
- }
1257
- }
1258
- ];
1259
- }
1260
-
1261
- // src/tools/calendar.ts
1262
-
1263
- function calendarTools(canvas) {
1264
- return [
1229
+ annotations: { readOnlyHint: true, openWorldHint: true },
1230
+ handler: async (params) => canvas.outcomes.listGroupOutcomes(
1231
+ params.context_type,
1232
+ params.context_id,
1233
+ params.outcome_group_id,
1234
+ {
1235
+ outcome_style: params.outcome_style
1236
+ }
1237
+ )
1238
+ },
1265
1239
  {
1266
- name: "list_calendar_events",
1267
- description: "List calendar events for a course.",
1240
+ name: "list_outcome_group_subgroups",
1241
+ description: "List the immediate child outcome groups under a specific outcome group.",
1268
1242
  inputSchema: {
1269
- course_id: _zod.z.number().describe("The Canvas course ID")
1270
- },
1271
- annotations: {
1272
- readOnlyHint: true,
1273
- openWorldHint: true
1243
+ context_type: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_CONTEXT_TYPES).describe("The outcome group context type."),
1244
+ context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
1245
+ outcome_group_id: _zod.z.number().describe("The Canvas outcome group ID.")
1274
1246
  },
1275
- handler: async (params) => {
1276
- const course_id = params.course_id;
1277
- return canvas.calendar.list(course_id);
1278
- }
1247
+ annotations: { readOnlyHint: true, openWorldHint: true },
1248
+ handler: async (params) => canvas.outcomes.listGroupSubgroups(
1249
+ params.context_type,
1250
+ params.context_id,
1251
+ params.outcome_group_id
1252
+ )
1279
1253
  },
1280
1254
  {
1281
- name: "create_calendar_event",
1282
- description: "Create a new calendar event in Canvas.",
1255
+ name: "get_outcome",
1256
+ description: "Get the full details for a specific learning outcome by ID.",
1283
1257
  inputSchema: {
1284
- context_code: _zod.z.string().describe('Canvas context code, e.g. "course_123" for a course event'),
1285
- title: _zod.z.string().describe("Event title"),
1286
- start_at: _zod.z.string().describe('Start time in ISO 8601 format, e.g. "2026-05-01T10:00:00Z"'),
1287
- end_at: _zod.z.string().optional().describe("End time in ISO 8601 format. Omit for all-day events."),
1288
- description: _zod.z.string().optional().describe("Event description (HTML allowed)"),
1289
- location_name: _zod.z.string().optional().describe("Location name")
1258
+ outcome_id: _zod.z.number().describe("The Canvas outcome ID."),
1259
+ add_defaults: _zod.z.boolean().optional().describe("Include default mastery colors and levels when Canvas supports it.")
1290
1260
  },
1291
- annotations: {
1292
- destructiveHint: true,
1293
- openWorldHint: true
1261
+ annotations: { readOnlyHint: true, openWorldHint: true },
1262
+ handler: async (params) => canvas.outcomes.getOutcome(params.outcome_id, {
1263
+ add_defaults: params.add_defaults
1264
+ })
1265
+ },
1266
+ {
1267
+ name: "get_outcome_alignments",
1268
+ description: "Get outcome alignments for a course, optionally filtered to a specific student or assignment.",
1269
+ inputSchema: {
1270
+ course_id: _zod.z.number().describe("The Canvas course ID."),
1271
+ student_id: _zod.z.number().optional().describe("Optional Canvas user ID of the student to filter alignments by."),
1272
+ assignment_id: _zod.z.number().optional().describe("Optional Canvas assignment ID to filter alignments by.")
1294
1273
  },
1295
- handler: async (params) => {
1296
- return canvas.calendar.createEvent({
1297
- context_code: params.context_code,
1298
- title: params.title,
1299
- start_at: params.start_at,
1300
- end_at: params.end_at,
1301
- description: params.description,
1302
- location_name: params.location_name
1303
- });
1304
- }
1274
+ annotations: { readOnlyHint: true, openWorldHint: true },
1275
+ handler: async (params) => canvas.outcomes.getOutcomeAlignments(params.course_id, {
1276
+ student_id: params.student_id,
1277
+ assignment_id: params.assignment_id
1278
+ })
1305
1279
  },
1306
1280
  {
1307
- name: "update_calendar_event",
1308
- description: "Update an existing calendar event. Only provided fields are changed.",
1281
+ name: "get_outcome_results",
1282
+ description: "Get per-student outcome results for a course, with optional outcome, student, and alignment filters.",
1309
1283
  inputSchema: {
1310
- event_id: _zod.z.number().describe("The Canvas calendar event ID"),
1311
- title: _zod.z.string().optional().describe("New event title"),
1312
- start_at: _zod.z.string().optional().describe("New start time in ISO 8601 format"),
1313
- end_at: _zod.z.string().optional().describe("New end time in ISO 8601 format"),
1314
- description: _zod.z.string().optional().describe("New description (HTML allowed)"),
1315
- location_name: _zod.z.string().optional().describe("New location name")
1284
+ course_id: _zod.z.number().describe("The Canvas course ID."),
1285
+ 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:".'),
1286
+ outcome_ids: _zod.z.array(_zod.z.number()).optional().describe("Optional outcome IDs to restrict the results."),
1287
+ include_alignments: _zod.z.boolean().optional().describe("Include linked alignment details in the response."),
1288
+ include_hidden: _zod.z.boolean().optional().describe("Include hidden outcomes when Canvas supports it.")
1316
1289
  },
1317
- annotations: {
1318
- destructiveHint: true,
1319
- openWorldHint: true
1290
+ annotations: { readOnlyHint: true, openWorldHint: true },
1291
+ handler: async (params) => canvas.outcomes.getOutcomeResults(params.course_id, {
1292
+ user_ids: params.user_ids,
1293
+ outcome_ids: params.outcome_ids,
1294
+ include_alignments: params.include_alignments,
1295
+ include_hidden: params.include_hidden
1296
+ })
1297
+ },
1298
+ {
1299
+ name: "get_outcome_rollups",
1300
+ description: "Get outcome rollups for a course, optionally aggregated or filtered by students, outcomes, and sort options.",
1301
+ inputSchema: {
1302
+ course_id: _zod.z.number().describe("The Canvas course ID."),
1303
+ aggregate: _zod.z.enum(["course"]).optional().describe("Aggregate all student rollups into a single course-level rollup."),
1304
+ aggregate_stat: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_ROLLUP_AGGREGATE_STATS).optional().describe('Statistic to use when aggregate="course".'),
1305
+ 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:".'),
1306
+ outcome_ids: _zod.z.array(_zod.z.number()).optional().describe("Optional outcome IDs to restrict the rollups."),
1307
+ include_courses: _zod.z.boolean().optional().describe("Include linked course details in the response payload."),
1308
+ exclude: _zod.z.array(_zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_EXCLUDE_OPTIONS)).optional().describe("Optional rollup exclusions for missing users or missing outcome results."),
1309
+ sort_by: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_ROLLUP_SORT_BY).optional().describe("Sort rollups by student name or by a specific outcome score."),
1310
+ sort_outcome_id: _zod.z.number().optional().describe('Outcome ID to sort by when sort_by="outcome".'),
1311
+ sort_order: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_SORT_ORDER).optional().describe("Sort order to apply when sorting rollups."),
1312
+ add_defaults: _zod.z.boolean().optional().describe("Include default mastery colors and levels when Canvas supports it.")
1320
1313
  },
1321
- handler: async (params) => {
1322
- const event_id = params.event_id;
1323
- return canvas.calendar.updateEvent(event_id, {
1324
- title: params.title,
1325
- start_at: params.start_at,
1326
- end_at: params.end_at,
1327
- description: params.description,
1328
- location_name: params.location_name
1329
- });
1330
- }
1314
+ annotations: { readOnlyHint: true, openWorldHint: true },
1315
+ handler: async (params) => canvas.outcomes.getOutcomeRollups(params.course_id, {
1316
+ aggregate: params.aggregate,
1317
+ aggregate_stat: params.aggregate_stat,
1318
+ user_ids: params.user_ids,
1319
+ outcome_ids: params.outcome_ids,
1320
+ include_courses: params.include_courses,
1321
+ exclude: params.exclude,
1322
+ sort_by: params.sort_by,
1323
+ sort_outcome_id: params.sort_outcome_id,
1324
+ sort_order: params.sort_order,
1325
+ add_defaults: params.add_defaults
1326
+ })
1327
+ },
1328
+ {
1329
+ name: "get_outcome_contributing_scores",
1330
+ description: "Get assignment or quiz scores that contributed to a specific outcome for one or more students in a course.",
1331
+ inputSchema: {
1332
+ course_id: _zod.z.number().describe("The Canvas course ID."),
1333
+ outcome_id: _zod.z.number().describe("The Canvas outcome ID."),
1334
+ 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:".'),
1335
+ only_assignment_alignments: _zod.z.boolean().optional().describe("Limit results to assignment alignments only."),
1336
+ show_unpublished_assignments: _zod.z.boolean().optional().describe("Include unpublished assignments in the contributing score results.")
1337
+ },
1338
+ annotations: { readOnlyHint: true, openWorldHint: true },
1339
+ handler: async (params) => canvas.outcomes.getOutcomeContributingScores(
1340
+ params.course_id,
1341
+ params.outcome_id,
1342
+ {
1343
+ user_ids: params.user_ids,
1344
+ only_assignment_alignments: params.only_assignment_alignments,
1345
+ show_unpublished_assignments: params.show_unpublished_assignments
1346
+ }
1347
+ )
1348
+ },
1349
+ {
1350
+ name: "get_outcome_mastery_distribution",
1351
+ description: "Get mastery distribution analytics for outcomes in a course, optionally filtered by students or outcomes.",
1352
+ inputSchema: {
1353
+ course_id: _zod.z.number().describe("The Canvas course ID."),
1354
+ exclude: _zod.z.array(_zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_EXCLUDE_OPTIONS)).optional().describe("Optional exclusions for missing users or missing outcome results."),
1355
+ outcome_ids: _zod.z.array(_zod.z.number()).optional().describe("Optional outcome IDs to restrict the distribution results."),
1356
+ 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:".'),
1357
+ include_alignment_distributions: _zod.z.boolean().optional().describe("Include contributing score distributions for alignments."),
1358
+ only_assignment_alignments: _zod.z.boolean().optional().describe("When including alignment distributions, limit them to assignments only."),
1359
+ show_unpublished_assignments: _zod.z.boolean().optional().describe("Include unpublished assignments in alignment distributions."),
1360
+ add_defaults: _zod.z.boolean().optional().describe("Include default mastery colors and levels when Canvas supports it.")
1361
+ },
1362
+ annotations: { readOnlyHint: true, openWorldHint: true },
1363
+ handler: async (params) => canvas.outcomes.getOutcomeMasteryDistribution(params.course_id, {
1364
+ exclude: params.exclude,
1365
+ outcome_ids: params.outcome_ids,
1366
+ student_ids: params.student_ids,
1367
+ include_alignment_distributions: params.include_alignment_distributions,
1368
+ only_assignment_alignments: params.only_assignment_alignments,
1369
+ show_unpublished_assignments: params.show_unpublished_assignments,
1370
+ add_defaults: params.add_defaults
1371
+ })
1331
1372
  }
1332
1373
  ];
1333
1374
  }
1334
1375
 
1335
- // src/tools/conversations.ts
1376
+ // src/tools/pages.ts
1336
1377
 
1337
- function conversationTools(canvas) {
1378
+ function pageTools(canvas) {
1338
1379
  return [
1339
1380
  {
1340
- name: "list_conversations",
1341
- description: "List conversations (inbox messages) for the authenticated user.",
1342
- inputSchema: {},
1381
+ name: "list_pages",
1382
+ description: "List all wiki pages in a course.",
1383
+ inputSchema: {
1384
+ course_id: _zod.z.number().describe("The Canvas course ID")
1385
+ },
1343
1386
  annotations: {
1344
1387
  readOnlyHint: true,
1345
1388
  openWorldHint: true
1346
1389
  },
1347
- handler: async () => {
1348
- return canvas.conversations.list();
1390
+ handler: async (params) => {
1391
+ const course_id = params.course_id;
1392
+ return canvas.pages.list(course_id);
1349
1393
  }
1350
1394
  },
1351
1395
  {
1352
- name: "get_conversation",
1353
- description: "Get a single conversation with its full message thread.",
1396
+ name: "get_page",
1397
+ description: "Get a single wiki page by its URL slug.",
1354
1398
  inputSchema: {
1355
- conversation_id: _zod.z.number().describe("The conversation ID")
1399
+ course_id: _zod.z.number().describe("The Canvas course ID"),
1400
+ page_url: _zod.z.string().describe('The page URL slug (e.g. "welcome-page")')
1356
1401
  },
1357
1402
  annotations: {
1358
1403
  readOnlyHint: true,
1359
1404
  openWorldHint: true
1360
1405
  },
1361
1406
  handler: async (params) => {
1362
- return canvas.conversations.get(params.conversation_id);
1407
+ const course_id = params.course_id;
1408
+ const page_url = params.page_url;
1409
+ return canvas.pages.get(course_id, page_url);
1363
1410
  }
1364
1411
  },
1365
1412
  {
1366
- name: "get_conversation_unread_count",
1367
- description: "Get the number of unread conversations for the authenticated user.",
1368
- inputSchema: {},
1413
+ name: "create_page",
1414
+ description: "Create a new wiki page in a course.",
1415
+ inputSchema: {
1416
+ course_id: _zod.z.number().describe("The Canvas course ID"),
1417
+ title: _zod.z.string().describe("Title of the page"),
1418
+ body: _zod.z.string().optional().describe("HTML body content of the page"),
1419
+ published: _zod.z.boolean().optional().describe("Whether the page is published"),
1420
+ editing_roles: _zod.z.string().optional().describe('Who can edit: "teachers", "students", "members", or "public"')
1421
+ },
1369
1422
  annotations: {
1370
- readOnlyHint: true,
1423
+ destructiveHint: true,
1371
1424
  openWorldHint: true
1372
1425
  },
1373
- handler: async () => {
1374
- return canvas.conversations.getUnreadCount();
1426
+ handler: async (params) => {
1427
+ const course_id = params.course_id;
1428
+ return canvas.pages.create(course_id, {
1429
+ title: params.title,
1430
+ body: params.body,
1431
+ published: params.published,
1432
+ editing_roles: params.editing_roles
1433
+ });
1375
1434
  }
1376
1435
  },
1377
1436
  {
1378
- name: "send_conversation",
1379
- description: "Send a new conversation message to one or more recipients.",
1437
+ name: "update_page",
1438
+ description: "Update an existing wiki page.",
1380
1439
  inputSchema: {
1381
- recipients: _zod.z.array(_zod.z.string()).describe("Array of recipient user IDs (as strings)"),
1382
- subject: _zod.z.string().describe("The conversation subject line"),
1383
- body: _zod.z.string().describe("The message body")
1440
+ course_id: _zod.z.number().describe("The Canvas course ID"),
1441
+ page_url: _zod.z.string().describe("The page URL slug"),
1442
+ title: _zod.z.string().optional().describe("New title for the page"),
1443
+ body: _zod.z.string().optional().describe("New HTML body content"),
1444
+ published: _zod.z.boolean().optional().describe("Whether the page is published"),
1445
+ editing_roles: _zod.z.string().optional().describe('Who can edit: "teachers", "students", "members", or "public"')
1384
1446
  },
1385
1447
  annotations: {
1386
1448
  destructiveHint: true,
1387
1449
  openWorldHint: true
1388
1450
  },
1389
1451
  handler: async (params) => {
1390
- const recipients = params.recipients;
1391
- const subject = params.subject;
1392
- const body = params.body;
1393
- return canvas.conversations.send(recipients, subject, body);
1452
+ const course_id = params.course_id;
1453
+ const page_url = params.page_url;
1454
+ return canvas.pages.update(course_id, page_url, {
1455
+ title: params.title,
1456
+ body: params.body,
1457
+ published: params.published,
1458
+ editing_roles: params.editing_roles
1459
+ });
1460
+ }
1461
+ },
1462
+ {
1463
+ name: "delete_page",
1464
+ description: "Delete a wiki page from a course.",
1465
+ inputSchema: {
1466
+ course_id: _zod.z.number().describe("The Canvas course ID"),
1467
+ page_url: _zod.z.string().describe("The page URL slug to delete")
1468
+ },
1469
+ annotations: {
1470
+ destructiveHint: true,
1471
+ openWorldHint: true
1472
+ },
1473
+ handler: async (params) => {
1474
+ const course_id = params.course_id;
1475
+ const page_url = params.page_url;
1476
+ await canvas.pages.delete(course_id, page_url);
1477
+ return { deleted: true, page_url };
1394
1478
  }
1395
1479
  }
1396
1480
  ];
@@ -1481,151 +1565,156 @@ function peerReviewTools(canvas) {
1481
1565
  ];
1482
1566
  }
1483
1567
 
1484
- // src/tools/accounts.ts
1568
+ // src/tools/quizzes.ts
1485
1569
 
1486
- function accountTools(canvas) {
1570
+ function quizTools(canvas) {
1487
1571
  return [
1488
1572
  {
1489
- name: "get_account",
1490
- description: "Get details for a Canvas account by ID.",
1573
+ name: "list_quizzes",
1574
+ description: "List all quizzes in a course.",
1491
1575
  inputSchema: {
1492
- account_id: _zod.z.number().describe("The Canvas account ID")
1576
+ course_id: _zod.z.number().describe("The Canvas course ID")
1493
1577
  },
1494
1578
  annotations: {
1495
1579
  readOnlyHint: true,
1496
1580
  openWorldHint: true
1497
1581
  },
1498
1582
  handler: async (params) => {
1499
- return canvas.accounts.get(params.account_id);
1583
+ const course_id = params.course_id;
1584
+ return canvas.quizzes.list(course_id);
1500
1585
  }
1501
1586
  },
1502
1587
  {
1503
- name: "list_accounts",
1504
- description: "List all accounts accessible to the authenticated user.",
1505
- inputSchema: {},
1588
+ name: "get_quiz",
1589
+ description: "Get details for a single quiz by ID.",
1590
+ inputSchema: {
1591
+ course_id: _zod.z.number().describe("The Canvas course ID"),
1592
+ quiz_id: _zod.z.number().describe("The Canvas quiz ID")
1593
+ },
1506
1594
  annotations: {
1507
1595
  readOnlyHint: true,
1508
1596
  openWorldHint: true
1509
1597
  },
1510
- handler: async () => {
1511
- return canvas.accounts.list();
1598
+ handler: async (params) => {
1599
+ const course_id = params.course_id;
1600
+ const quiz_id = params.quiz_id;
1601
+ return canvas.quizzes.get(course_id, quiz_id);
1512
1602
  }
1513
1603
  },
1514
1604
  {
1515
- name: "list_sub_accounts",
1516
- description: "List sub-accounts under a given Canvas account.",
1605
+ name: "list_quiz_submissions",
1606
+ description: "List all submissions for a quiz.",
1517
1607
  inputSchema: {
1518
- account_id: _zod.z.number().describe("The parent Canvas account ID")
1608
+ course_id: _zod.z.number().describe("The Canvas course ID"),
1609
+ quiz_id: _zod.z.number().describe("The Canvas quiz ID")
1519
1610
  },
1520
1611
  annotations: {
1521
1612
  readOnlyHint: true,
1522
1613
  openWorldHint: true
1523
1614
  },
1524
1615
  handler: async (params) => {
1525
- return canvas.accounts.listSubAccounts(params.account_id);
1616
+ const course_id = params.course_id;
1617
+ const quiz_id = params.quiz_id;
1618
+ return canvas.quizzes.listSubmissions(course_id, quiz_id);
1526
1619
  }
1527
1620
  },
1528
1621
  {
1529
- name: "list_account_courses",
1530
- description: "List courses under a given Canvas account.",
1622
+ name: "list_quiz_questions",
1623
+ description: "List all questions in a quiz.",
1531
1624
  inputSchema: {
1532
- account_id: _zod.z.number().describe("The Canvas account ID"),
1533
- search_term: _zod.z.string().optional().describe("Search courses by name or course code")
1625
+ course_id: _zod.z.number().describe("The Canvas course ID"),
1626
+ quiz_id: _zod.z.number().describe("The Canvas quiz ID")
1534
1627
  },
1535
1628
  annotations: {
1536
1629
  readOnlyHint: true,
1537
1630
  openWorldHint: true
1538
1631
  },
1539
1632
  handler: async (params) => {
1540
- return canvas.accounts.listCourses(params.account_id, {
1541
- search_term: params.search_term
1542
- });
1633
+ const course_id = params.course_id;
1634
+ const quiz_id = params.quiz_id;
1635
+ return canvas.quizzes.listQuestions(course_id, quiz_id);
1543
1636
  }
1544
1637
  },
1545
1638
  {
1546
- name: "list_account_users",
1547
- description: "List users in a Canvas account.",
1639
+ name: "get_quiz_submission_answers",
1640
+ description: "Get a student's answers for a quiz submission.",
1548
1641
  inputSchema: {
1549
- account_id: _zod.z.number().describe("The Canvas account ID"),
1550
- search_term: _zod.z.string().optional().describe("Search users by name, email, or login")
1642
+ quiz_submission_id: _zod.z.number().describe("The Canvas quiz submission ID")
1551
1643
  },
1552
1644
  annotations: {
1553
1645
  readOnlyHint: true,
1554
1646
  openWorldHint: true
1555
1647
  },
1556
1648
  handler: async (params) => {
1557
- return canvas.accounts.listUsers(params.account_id, {
1558
- search_term: params.search_term
1559
- });
1649
+ const quiz_submission_id = params.quiz_submission_id;
1650
+ return canvas.quizzes.getSubmissionAnswers(quiz_submission_id);
1560
1651
  }
1561
1652
  },
1562
1653
  {
1563
- name: "get_account_reports",
1564
- description: "List available report types for a Canvas account.",
1654
+ name: "score_quiz_question",
1655
+ description: "Score a specific question in a quiz submission. Specify attempt to score a particular attempt (omit for latest). Requires grading permissions.",
1565
1656
  inputSchema: {
1566
- account_id: _zod.z.number().describe("The Canvas account ID")
1657
+ course_id: _zod.z.number().describe("The Canvas course ID"),
1658
+ quiz_id: _zod.z.number().describe("The Canvas quiz ID"),
1659
+ submission_id: _zod.z.number().describe("The Canvas quiz submission ID"),
1660
+ question_id: _zod.z.number().describe("The Canvas quiz question ID"),
1661
+ score: _zod.z.number().describe("The score to assign"),
1662
+ comment: _zod.z.string().optional().describe("Optional feedback comment"),
1663
+ attempt: _zod.z.number().optional().describe("Quiz attempt number to score (omit for latest)")
1567
1664
  },
1568
1665
  annotations: {
1569
- readOnlyHint: true,
1666
+ destructiveHint: true,
1667
+ idempotentHint: true,
1570
1668
  openWorldHint: true
1571
1669
  },
1572
1670
  handler: async (params) => {
1573
- return canvas.accounts.getReports(params.account_id);
1671
+ const course_id = params.course_id;
1672
+ const quiz_id = params.quiz_id;
1673
+ const submission_id = params.submission_id;
1674
+ const question_id = params.question_id;
1675
+ const score = params.score;
1676
+ const comment = params.comment;
1677
+ const attempt = params.attempt;
1678
+ await canvas.quizzes.scoreQuestion(
1679
+ course_id,
1680
+ quiz_id,
1681
+ submission_id,
1682
+ question_id,
1683
+ score,
1684
+ comment,
1685
+ attempt
1686
+ );
1687
+ return { success: true };
1574
1688
  }
1575
1689
  }
1576
1690
  ];
1577
1691
  }
1578
1692
 
1579
- // src/tools/analytics.ts
1693
+ // src/tools/rubrics.ts
1580
1694
 
1581
- function analyticsTools(canvas) {
1695
+ function rubricTools(canvas) {
1582
1696
  return [
1583
1697
  {
1584
- name: "search_course_content",
1585
- description: "Search for content within a course. Searches pages, assignments, discussions, and announcements by keyword.",
1698
+ name: "list_rubrics",
1699
+ description: "List all rubrics in a course.",
1586
1700
  inputSchema: {
1587
- course_id: _zod.z.number().describe("The Canvas course ID"),
1588
- search_term: _zod.z.string().describe("The keyword or phrase to search for"),
1589
- content_types: _zod.z.array(_zod.z.enum(_chunkXAOI3FO6cjs.SEARCH_CONTENT_TYPES)).optional().describe(
1590
- "Content types to search. Defaults to all types: pages, discussions, assignments, announcements."
1591
- )
1701
+ course_id: _zod.z.number().describe("The Canvas course ID")
1592
1702
  },
1593
1703
  annotations: {
1594
1704
  readOnlyHint: true,
1595
1705
  openWorldHint: true
1596
1706
  },
1597
1707
  handler: async (params) => {
1598
- const courseId = params.course_id;
1599
- const searchTerm = params.search_term;
1600
- const types = _nullishCoalesce(params.content_types, () => ( [
1601
- ..._chunkXAOI3FO6cjs.SEARCH_CONTENT_TYPES
1602
- ]));
1603
- if (types.length === 0) return { results: [] };
1604
- const settled = await Promise.allSettled(
1605
- types.map((type) => canvas.analytics.searchContentType(courseId, searchTerm, type))
1606
- );
1607
- const fulfilled = settled.filter(
1608
- (r) => r.status === "fulfilled"
1609
- );
1610
- if (fulfilled.length === 0) {
1611
- const allErrors = settled.map(
1612
- (result, index) => result.status === "rejected" ? `${types[index]}: ${formatError(result.reason)}` : null
1613
- ).filter((errorLine) => errorLine !== null);
1614
- throw new Error(`All content type searches failed:
1615
- ${allErrors.join("\n")}`);
1616
- }
1617
- const results = fulfilled.flatMap((r) => r.value);
1618
- const warnings = settled.map(
1619
- (r, i) => r.status === "rejected" ? `${types[i]} search failed: ${formatError(r.reason)}` : null
1620
- ).filter((w) => w !== null);
1621
- return warnings.length > 0 ? { results, warnings } : { results };
1708
+ const course_id = params.course_id;
1709
+ return canvas.rubrics.list(course_id);
1622
1710
  }
1623
1711
  },
1624
1712
  {
1625
- name: "get_course_analytics",
1626
- description: "Get course-level activity analytics. Returns daily page view and participation counts.",
1713
+ name: "get_rubric",
1714
+ description: "Get details for a single rubric by ID, including criteria.",
1627
1715
  inputSchema: {
1628
- course_id: _zod.z.number().describe("The Canvas course ID")
1716
+ course_id: _zod.z.number().describe("The Canvas course ID"),
1717
+ rubric_id: _zod.z.number().describe("The Canvas rubric ID")
1629
1718
  },
1630
1719
  annotations: {
1631
1720
  readOnlyHint: true,
@@ -1633,15 +1722,17 @@ ${allErrors.join("\n")}`);
1633
1722
  },
1634
1723
  handler: async (params) => {
1635
1724
  const course_id = params.course_id;
1636
- return canvas.analytics.getCourseActivity(course_id);
1725
+ const rubric_id = params.rubric_id;
1726
+ return canvas.rubrics.get(course_id, rubric_id);
1637
1727
  }
1638
1728
  },
1639
1729
  {
1640
- name: "get_student_analytics",
1641
- description: "Get per-student activity analytics for a course. Returns page views, participations, and submission timeline for a specific student.",
1730
+ name: "get_rubric_assessment",
1731
+ description: "Get the rubric assessment for a specific student submission on an assignment.",
1642
1732
  inputSchema: {
1643
1733
  course_id: _zod.z.number().describe("The Canvas course ID"),
1644
- student_id: _zod.z.number().describe("The Canvas user ID of the student")
1734
+ assignment_id: _zod.z.number().describe("The Canvas assignment ID"),
1735
+ user_id: _zod.z.number().describe("The Canvas user ID")
1645
1736
  },
1646
1737
  annotations: {
1647
1738
  readOnlyHint: true,
@@ -1649,23 +1740,35 @@ ${allErrors.join("\n")}`);
1649
1740
  },
1650
1741
  handler: async (params) => {
1651
1742
  const course_id = params.course_id;
1652
- const student_id = params.student_id;
1653
- return canvas.analytics.getStudentActivity(course_id, student_id);
1743
+ const assignment_id = params.assignment_id;
1744
+ const user_id = params.user_id;
1745
+ return canvas.rubrics.getAssessment(course_id, assignment_id, user_id);
1654
1746
  }
1655
1747
  },
1656
1748
  {
1657
- name: "get_course_activity_stream",
1658
- description: "Get a summary of recent activity in a course. Returns counts of recent events grouped by type (submissions, discussions, announcements, etc.).",
1749
+ name: "submit_rubric_assessment",
1750
+ description: "Submit a rubric assessment with scores and comments for each criterion.",
1659
1751
  inputSchema: {
1660
- course_id: _zod.z.number().describe("The Canvas course ID")
1752
+ course_id: _zod.z.number().describe("The Canvas course ID"),
1753
+ association_id: _zod.z.number().describe("The rubric association ID"),
1754
+ data: _zod.z.array(
1755
+ _zod.z.object({
1756
+ criterion_id: _zod.z.string().describe("The rubric criterion ID"),
1757
+ points: _zod.z.number().describe("Points awarded for this criterion"),
1758
+ comments: _zod.z.string().describe("Feedback comment for this criterion")
1759
+ })
1760
+ ).describe("Array of criterion assessments")
1661
1761
  },
1662
1762
  annotations: {
1663
- readOnlyHint: true,
1763
+ destructiveHint: true,
1764
+ idempotentHint: true,
1664
1765
  openWorldHint: true
1665
1766
  },
1666
1767
  handler: async (params) => {
1667
1768
  const course_id = params.course_id;
1668
- return canvas.analytics.getCourseActivityStream(course_id);
1769
+ const association_id = params.association_id;
1770
+ const data = params.data;
1771
+ return canvas.rubrics.submitAssessment(course_id, association_id, data);
1669
1772
  }
1670
1773
  }
1671
1774
  ];
@@ -1732,307 +1835,293 @@ function studentTools(canvas) {
1732
1835
  ];
1733
1836
  }
1734
1837
 
1735
- // src/tools/dashboard.ts
1736
- function dashboardTools(canvas) {
1838
+ // src/tools/submissions.ts
1839
+
1840
+ function submissionTools(canvas) {
1737
1841
  return [
1738
1842
  {
1739
- name: "get_dashboard_cards",
1740
- description: "Get the current user's dashboard course cards with position, color, and image.",
1741
- inputSchema: {},
1843
+ name: "list_submissions",
1844
+ description: "List all submissions for an assignment in a course.",
1845
+ inputSchema: {
1846
+ course_id: _zod.z.number().describe("The Canvas course ID"),
1847
+ assignment_id: _zod.z.number().describe("The Canvas assignment ID")
1848
+ },
1742
1849
  annotations: {
1743
1850
  readOnlyHint: true,
1744
1851
  openWorldHint: true
1745
1852
  },
1746
- handler: async () => {
1747
- return canvas.dashboard.getDashboardCards();
1853
+ handler: async (params) => {
1854
+ const course_id = params.course_id;
1855
+ const assignment_id = params.assignment_id;
1856
+ return canvas.submissions.list(course_id, assignment_id);
1748
1857
  }
1749
1858
  },
1750
1859
  {
1751
- name: "get_todo_items",
1752
- description: "Get the current user's to-do items, including upcoming assignments and grading tasks.",
1753
- inputSchema: {},
1860
+ name: "get_submission",
1861
+ description: "Get a single submission for a specific user on an assignment, including comments.",
1862
+ inputSchema: {
1863
+ course_id: _zod.z.number().describe("The Canvas course ID"),
1864
+ assignment_id: _zod.z.number().describe("The Canvas assignment ID"),
1865
+ user_id: _zod.z.number().describe("The Canvas user ID")
1866
+ },
1754
1867
  annotations: {
1755
1868
  readOnlyHint: true,
1756
1869
  openWorldHint: true
1757
1870
  },
1758
- handler: async () => {
1759
- return canvas.dashboard.getTodoItems();
1871
+ handler: async (params) => {
1872
+ const course_id = params.course_id;
1873
+ const assignment_id = params.assignment_id;
1874
+ const user_id = params.user_id;
1875
+ return canvas.submissions.get(course_id, assignment_id, user_id);
1760
1876
  }
1761
1877
  },
1762
1878
  {
1763
- name: "get_upcoming_events",
1764
- description: "Get the current user's upcoming calendar events and assignments.",
1765
- inputSchema: {},
1879
+ name: "grade_submission",
1880
+ description: "Post or update a grade for a submission. Requires grading permissions.",
1881
+ inputSchema: {
1882
+ course_id: _zod.z.number().describe("The Canvas course ID"),
1883
+ assignment_id: _zod.z.number().describe("The Canvas assignment ID"),
1884
+ user_id: _zod.z.number().describe("The Canvas user ID"),
1885
+ grade: _zod.z.string().describe('The grade to assign (e.g. "95", "A", "pass")')
1886
+ },
1766
1887
  annotations: {
1767
- readOnlyHint: true,
1888
+ destructiveHint: true,
1889
+ idempotentHint: true,
1768
1890
  openWorldHint: true
1769
1891
  },
1770
- handler: async () => {
1771
- return canvas.dashboard.getUpcomingEvents();
1892
+ handler: async (params) => {
1893
+ const course_id = params.course_id;
1894
+ const assignment_id = params.assignment_id;
1895
+ const user_id = params.user_id;
1896
+ const grade = params.grade;
1897
+ return canvas.submissions.grade(course_id, assignment_id, user_id, grade);
1772
1898
  }
1773
1899
  },
1774
1900
  {
1775
- name: "get_missing_submissions",
1776
- description: "Get assignments with missing submissions for the current user.",
1777
- inputSchema: {},
1901
+ name: "comment_on_submission",
1902
+ description: "Add a text comment to a submission.",
1903
+ inputSchema: {
1904
+ course_id: _zod.z.number().describe("The Canvas course ID"),
1905
+ assignment_id: _zod.z.number().describe("The Canvas assignment ID"),
1906
+ user_id: _zod.z.number().describe("The Canvas user ID"),
1907
+ comment: _zod.z.string().describe("The comment text to add")
1908
+ },
1778
1909
  annotations: {
1779
- readOnlyHint: true,
1910
+ destructiveHint: true,
1780
1911
  openWorldHint: true
1781
1912
  },
1782
- handler: async () => {
1783
- return canvas.dashboard.getMissingSubmissions();
1913
+ handler: async (params) => {
1914
+ const course_id = params.course_id;
1915
+ const assignment_id = params.assignment_id;
1916
+ const user_id = params.user_id;
1917
+ const comment = params.comment;
1918
+ return canvas.submissions.comment(course_id, assignment_id, user_id, comment);
1784
1919
  }
1785
1920
  }
1786
1921
  ];
1787
1922
  }
1788
1923
 
1789
- // src/tools/outcomes.ts
1924
+ // src/tools/users.ts
1790
1925
 
1791
- function outcomeTools(canvas) {
1926
+ function userTools(canvas) {
1792
1927
  return [
1793
1928
  {
1794
- name: "get_root_outcome_group",
1795
- description: "Get the root outcome group for an account or course context.",
1796
- inputSchema: {
1797
- context_type: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_CONTEXT_TYPES).describe("Whether to read outcomes from an account or course context."),
1798
- context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context.")
1799
- },
1800
- annotations: { readOnlyHint: true, openWorldHint: true },
1801
- handler: async (params) => canvas.outcomes.getRootOutcomeGroup(
1802
- params.context_type,
1803
- params.context_id
1804
- )
1805
- },
1806
- {
1807
- name: "list_outcome_groups",
1808
- description: "List all outcome groups for an account or course context.",
1809
- inputSchema: {
1810
- context_type: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_CONTEXT_TYPES).describe("Whether to read outcome groups from an account or course context."),
1811
- context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context.")
1812
- },
1813
- annotations: { readOnlyHint: true, openWorldHint: true },
1814
- handler: async (params) => canvas.outcomes.listOutcomeGroups(
1815
- params.context_type,
1816
- params.context_id
1817
- )
1818
- },
1819
- {
1820
- name: "list_outcome_group_links",
1821
- description: "List all outcome links in an account or course context.",
1822
- inputSchema: {
1823
- context_type: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_CONTEXT_TYPES).describe("Whether to read outcome links from an account or course context."),
1824
- context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
1825
- outcome_style: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_DETAIL_LEVELS).optional().describe('Outcome detail level. Use "full" to include expanded outcome fields.'),
1826
- outcome_group_style: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_DETAIL_LEVELS).optional().describe(
1827
- 'Outcome group detail level. Use "full" to include expanded outcome group fields.'
1828
- )
1829
- },
1830
- annotations: { readOnlyHint: true, openWorldHint: true },
1831
- handler: async (params) => canvas.outcomes.listOutcomeGroupLinks(
1832
- params.context_type,
1833
- params.context_id,
1834
- {
1835
- outcome_style: params.outcome_style,
1836
- outcome_group_style: params.outcome_group_style
1837
- }
1838
- )
1839
- },
1840
- {
1841
- name: "get_outcome_group",
1842
- description: "Get details for a specific outcome group in an account or course context.",
1929
+ name: "list_students",
1930
+ description: "List all students enrolled in a course.",
1843
1931
  inputSchema: {
1844
- context_type: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_CONTEXT_TYPES).describe("The outcome group context type."),
1845
- context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
1846
- outcome_group_id: _zod.z.number().describe("The Canvas outcome group ID.")
1932
+ course_id: _zod.z.number().describe("The Canvas course ID")
1847
1933
  },
1848
- annotations: { readOnlyHint: true, openWorldHint: true },
1849
- handler: async (params) => canvas.outcomes.getOutcomeGroup(
1850
- params.context_type,
1851
- params.context_id,
1852
- params.outcome_group_id
1853
- )
1854
- },
1855
- {
1856
- name: "list_outcome_group_outcomes",
1857
- description: "List the linked outcomes directly under a specific outcome group.",
1858
- inputSchema: {
1859
- context_type: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_CONTEXT_TYPES).describe("The outcome group context type."),
1860
- context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
1861
- outcome_group_id: _zod.z.number().describe("The Canvas outcome group ID."),
1862
- outcome_style: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_DETAIL_LEVELS).optional().describe('Outcome detail level. Use "full" to include expanded outcome fields.')
1934
+ annotations: {
1935
+ readOnlyHint: true,
1936
+ openWorldHint: true
1863
1937
  },
1864
- annotations: { readOnlyHint: true, openWorldHint: true },
1865
- handler: async (params) => canvas.outcomes.listGroupOutcomes(
1866
- params.context_type,
1867
- params.context_id,
1868
- params.outcome_group_id,
1869
- {
1870
- outcome_style: params.outcome_style
1871
- }
1872
- )
1938
+ handler: async (params) => {
1939
+ const course_id = params.course_id;
1940
+ return canvas.users.listStudents(course_id);
1941
+ }
1873
1942
  },
1874
1943
  {
1875
- name: "list_outcome_group_subgroups",
1876
- description: "List the immediate child outcome groups under a specific outcome group.",
1944
+ name: "get_user",
1945
+ description: "Get details for a single user by ID.",
1877
1946
  inputSchema: {
1878
- context_type: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_CONTEXT_TYPES).describe("The outcome group context type."),
1879
- context_id: _zod.z.number().describe("The Canvas account ID or course ID for the context."),
1880
- outcome_group_id: _zod.z.number().describe("The Canvas outcome group ID.")
1947
+ user_id: _zod.z.number().describe("The Canvas user ID")
1881
1948
  },
1882
- annotations: { readOnlyHint: true, openWorldHint: true },
1883
- handler: async (params) => canvas.outcomes.listGroupSubgroups(
1884
- params.context_type,
1885
- params.context_id,
1886
- params.outcome_group_id
1887
- )
1888
- },
1889
- {
1890
- name: "get_outcome",
1891
- description: "Get the full details for a specific learning outcome by ID.",
1892
- inputSchema: {
1893
- outcome_id: _zod.z.number().describe("The Canvas outcome ID."),
1894
- add_defaults: _zod.z.boolean().optional().describe("Include default mastery colors and levels when Canvas supports it.")
1949
+ annotations: {
1950
+ readOnlyHint: true,
1951
+ openWorldHint: true
1895
1952
  },
1896
- annotations: { readOnlyHint: true, openWorldHint: true },
1897
- handler: async (params) => canvas.outcomes.getOutcome(params.outcome_id, {
1898
- add_defaults: params.add_defaults
1899
- })
1953
+ handler: async (params) => {
1954
+ const user_id = params.user_id;
1955
+ return canvas.users.get(user_id);
1956
+ }
1900
1957
  },
1901
1958
  {
1902
- name: "get_outcome_alignments",
1903
- description: "Get outcome alignments for a course, optionally filtered to a specific student or assignment.",
1904
- inputSchema: {
1905
- course_id: _zod.z.number().describe("The Canvas course ID."),
1906
- student_id: _zod.z.number().optional().describe("Optional Canvas user ID of the student to filter alignments by."),
1907
- assignment_id: _zod.z.number().optional().describe("Optional Canvas assignment ID to filter alignments by.")
1959
+ name: "get_profile",
1960
+ description: "Get the profile of the currently authenticated user.",
1961
+ inputSchema: {},
1962
+ annotations: {
1963
+ readOnlyHint: true,
1964
+ openWorldHint: true
1908
1965
  },
1909
- annotations: { readOnlyHint: true, openWorldHint: true },
1910
- handler: async (params) => canvas.outcomes.getOutcomeAlignments(params.course_id, {
1911
- student_id: params.student_id,
1912
- assignment_id: params.assignment_id
1913
- })
1966
+ handler: async () => {
1967
+ return canvas.users.getProfile();
1968
+ }
1914
1969
  },
1915
1970
  {
1916
- name: "get_outcome_results",
1917
- description: "Get per-student outcome results for a course, with optional outcome, student, and alignment filters.",
1971
+ name: "search_users",
1972
+ description: "Search for users in a Canvas account by name, login, or email.",
1918
1973
  inputSchema: {
1919
- course_id: _zod.z.number().describe("The Canvas course ID."),
1920
- 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:".'),
1921
- outcome_ids: _zod.z.array(_zod.z.number()).optional().describe("Optional outcome IDs to restrict the results."),
1922
- include_alignments: _zod.z.boolean().optional().describe("Include linked alignment details in the response."),
1923
- include_hidden: _zod.z.boolean().optional().describe("Include hidden outcomes when Canvas supports it.")
1974
+ account_id: _zod.z.number().describe("The Canvas account ID"),
1975
+ search_term: _zod.z.string().describe("The search term (name, login, or email)"),
1976
+ sort: _zod.z.enum(["username", "email", "sis_id", "last_login"]).optional().describe("Sort field"),
1977
+ order: _zod.z.enum(["asc", "desc"]).optional().describe("Sort order")
1924
1978
  },
1925
- annotations: { readOnlyHint: true, openWorldHint: true },
1926
- handler: async (params) => canvas.outcomes.getOutcomeResults(params.course_id, {
1927
- user_ids: params.user_ids,
1928
- outcome_ids: params.outcome_ids,
1929
- include_alignments: params.include_alignments,
1930
- include_hidden: params.include_hidden
1931
- })
1932
- },
1933
- {
1934
- name: "get_outcome_rollups",
1935
- description: "Get outcome rollups for a course, optionally aggregated or filtered by students, outcomes, and sort options.",
1936
- inputSchema: {
1937
- course_id: _zod.z.number().describe("The Canvas course ID."),
1938
- aggregate: _zod.z.enum(["course"]).optional().describe("Aggregate all student rollups into a single course-level rollup."),
1939
- aggregate_stat: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_ROLLUP_AGGREGATE_STATS).optional().describe('Statistic to use when aggregate="course".'),
1940
- 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:".'),
1941
- outcome_ids: _zod.z.array(_zod.z.number()).optional().describe("Optional outcome IDs to restrict the rollups."),
1942
- include_courses: _zod.z.boolean().optional().describe("Include linked course details in the response payload."),
1943
- exclude: _zod.z.array(_zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_EXCLUDE_OPTIONS)).optional().describe("Optional rollup exclusions for missing users or missing outcome results."),
1944
- sort_by: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_ROLLUP_SORT_BY).optional().describe("Sort rollups by student name or by a specific outcome score."),
1945
- sort_outcome_id: _zod.z.number().optional().describe('Outcome ID to sort by when sort_by="outcome".'),
1946
- sort_order: _zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_SORT_ORDER).optional().describe("Sort order to apply when sorting rollups."),
1947
- add_defaults: _zod.z.boolean().optional().describe("Include default mastery colors and levels when Canvas supports it.")
1979
+ annotations: {
1980
+ readOnlyHint: true,
1981
+ openWorldHint: true
1948
1982
  },
1949
- annotations: { readOnlyHint: true, openWorldHint: true },
1950
- handler: async (params) => canvas.outcomes.getOutcomeRollups(params.course_id, {
1951
- aggregate: params.aggregate,
1952
- aggregate_stat: params.aggregate_stat,
1953
- user_ids: params.user_ids,
1954
- outcome_ids: params.outcome_ids,
1955
- include_courses: params.include_courses,
1956
- exclude: params.exclude,
1957
- sort_by: params.sort_by,
1958
- sort_outcome_id: params.sort_outcome_id,
1959
- sort_order: params.sort_order,
1960
- add_defaults: params.add_defaults
1961
- })
1983
+ handler: async (params) => {
1984
+ return canvas.users.searchUsers(
1985
+ params.account_id,
1986
+ params.search_term,
1987
+ params.sort,
1988
+ params.order
1989
+ );
1990
+ }
1962
1991
  },
1963
1992
  {
1964
- name: "get_outcome_contributing_scores",
1965
- description: "Get assignment or quiz scores that contributed to a specific outcome for one or more students in a course.",
1993
+ name: "list_course_users",
1994
+ description: "List all users in a course, optionally filtered by enrollment type.",
1966
1995
  inputSchema: {
1967
- course_id: _zod.z.number().describe("The Canvas course ID."),
1968
- outcome_id: _zod.z.number().describe("The Canvas outcome ID."),
1969
- 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:".'),
1970
- only_assignment_alignments: _zod.z.boolean().optional().describe("Limit results to assignment alignments only."),
1971
- show_unpublished_assignments: _zod.z.boolean().optional().describe("Include unpublished assignments in the contributing score results.")
1996
+ course_id: _zod.z.number().describe("The Canvas course ID"),
1997
+ enrollment_type: _zod.z.enum(["student", "teacher", "ta", "observer", "designer"]).optional().describe("Filter by enrollment type")
1972
1998
  },
1973
- annotations: { readOnlyHint: true, openWorldHint: true },
1974
- handler: async (params) => canvas.outcomes.getOutcomeContributingScores(
1975
- params.course_id,
1976
- params.outcome_id,
1977
- {
1978
- user_ids: params.user_ids,
1979
- only_assignment_alignments: params.only_assignment_alignments,
1980
- show_unpublished_assignments: params.show_unpublished_assignments
1981
- }
1982
- )
1983
- },
1984
- {
1985
- name: "get_outcome_mastery_distribution",
1986
- description: "Get mastery distribution analytics for outcomes in a course, optionally filtered by students or outcomes.",
1987
- inputSchema: {
1988
- course_id: _zod.z.number().describe("The Canvas course ID."),
1989
- exclude: _zod.z.array(_zod.z.enum(_chunkXAOI3FO6cjs.OUTCOME_EXCLUDE_OPTIONS)).optional().describe("Optional exclusions for missing users or missing outcome results."),
1990
- outcome_ids: _zod.z.array(_zod.z.number()).optional().describe("Optional outcome IDs to restrict the distribution results."),
1991
- 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:".'),
1992
- include_alignment_distributions: _zod.z.boolean().optional().describe("Include contributing score distributions for alignments."),
1993
- only_assignment_alignments: _zod.z.boolean().optional().describe("When including alignment distributions, limit them to assignments only."),
1994
- show_unpublished_assignments: _zod.z.boolean().optional().describe("Include unpublished assignments in alignment distributions."),
1995
- add_defaults: _zod.z.boolean().optional().describe("Include default mastery colors and levels when Canvas supports it.")
1999
+ annotations: {
2000
+ readOnlyHint: true,
2001
+ openWorldHint: true
1996
2002
  },
1997
- annotations: { readOnlyHint: true, openWorldHint: true },
1998
- handler: async (params) => canvas.outcomes.getOutcomeMasteryDistribution(params.course_id, {
1999
- exclude: params.exclude,
2000
- outcome_ids: params.outcome_ids,
2001
- student_ids: params.student_ids,
2002
- include_alignment_distributions: params.include_alignment_distributions,
2003
- only_assignment_alignments: params.only_assignment_alignments,
2004
- show_unpublished_assignments: params.show_unpublished_assignments,
2005
- add_defaults: params.add_defaults
2006
- })
2003
+ handler: async (params) => {
2004
+ return canvas.users.listCourseUsers(
2005
+ params.course_id,
2006
+ params.enrollment_type
2007
+ );
2008
+ }
2007
2009
  }
2008
2010
  ];
2009
2011
  }
2010
2012
 
2013
+ // src/tools/catalog.ts
2014
+ var toolDomainCatalog = [
2015
+ {
2016
+ domain: "health",
2017
+ defaultPrimaryAudience: "shared",
2018
+ getTools: healthTools
2019
+ },
2020
+ {
2021
+ domain: "courses",
2022
+ defaultPrimaryAudience: "shared",
2023
+ getTools: courseTools
2024
+ },
2025
+ {
2026
+ domain: "assignments",
2027
+ defaultPrimaryAudience: "educator",
2028
+ getTools: assignmentTools
2029
+ },
2030
+ {
2031
+ domain: "submissions",
2032
+ defaultPrimaryAudience: "educator",
2033
+ getTools: submissionTools
2034
+ },
2035
+ {
2036
+ domain: "rubrics",
2037
+ defaultPrimaryAudience: "educator",
2038
+ getTools: rubricTools
2039
+ },
2040
+ {
2041
+ domain: "quizzes",
2042
+ defaultPrimaryAudience: "educator",
2043
+ getTools: quizTools
2044
+ },
2045
+ {
2046
+ domain: "files",
2047
+ defaultPrimaryAudience: "shared",
2048
+ getTools: fileTools
2049
+ },
2050
+ {
2051
+ domain: "users",
2052
+ defaultPrimaryAudience: "educator",
2053
+ getTools: userTools
2054
+ },
2055
+ {
2056
+ domain: "groups",
2057
+ defaultPrimaryAudience: "shared",
2058
+ getTools: groupTools
2059
+ },
2060
+ {
2061
+ domain: "enrollments",
2062
+ defaultPrimaryAudience: "admin",
2063
+ getTools: enrollmentTools
2064
+ },
2065
+ {
2066
+ domain: "discussions",
2067
+ defaultPrimaryAudience: "shared",
2068
+ getTools: discussionTools
2069
+ },
2070
+ {
2071
+ domain: "modules",
2072
+ defaultPrimaryAudience: "educator",
2073
+ getTools: moduleTools
2074
+ },
2075
+ {
2076
+ domain: "pages",
2077
+ defaultPrimaryAudience: "educator",
2078
+ getTools: pageTools
2079
+ },
2080
+ {
2081
+ domain: "calendar",
2082
+ defaultPrimaryAudience: "shared",
2083
+ getTools: calendarTools
2084
+ },
2085
+ {
2086
+ domain: "conversations",
2087
+ defaultPrimaryAudience: "shared",
2088
+ getTools: conversationTools
2089
+ },
2090
+ {
2091
+ domain: "peer_reviews",
2092
+ defaultPrimaryAudience: "shared",
2093
+ getTools: peerReviewTools
2094
+ },
2095
+ {
2096
+ domain: "accounts",
2097
+ defaultPrimaryAudience: "admin",
2098
+ getTools: accountTools
2099
+ },
2100
+ {
2101
+ domain: "analytics",
2102
+ defaultPrimaryAudience: "educator",
2103
+ getTools: analyticsTools
2104
+ },
2105
+ {
2106
+ domain: "outcomes",
2107
+ defaultPrimaryAudience: "educator",
2108
+ getTools: outcomeTools
2109
+ },
2110
+ {
2111
+ domain: "student",
2112
+ defaultPrimaryAudience: "student",
2113
+ getTools: studentTools
2114
+ },
2115
+ {
2116
+ domain: "dashboard",
2117
+ defaultPrimaryAudience: "student",
2118
+ getTools: dashboardTools
2119
+ }
2120
+ ];
2121
+
2011
2122
  // src/tools/index.ts
2012
2123
  function getAllTools(canvas) {
2013
- return [
2014
- ...healthTools(canvas),
2015
- ...courseTools(canvas),
2016
- ...assignmentTools(canvas),
2017
- ...submissionTools(canvas),
2018
- ...rubricTools(canvas),
2019
- ...quizTools(canvas),
2020
- ...fileTools(canvas),
2021
- ...userTools(canvas),
2022
- ...groupTools(canvas),
2023
- ...enrollmentTools(canvas),
2024
- ...discussionTools(canvas),
2025
- ...moduleTools(canvas),
2026
- ...pageTools(canvas),
2027
- ...calendarTools(canvas),
2028
- ...conversationTools(canvas),
2029
- ...peerReviewTools(canvas),
2030
- ...accountTools(canvas),
2031
- ...analyticsTools(canvas),
2032
- ...outcomeTools(canvas),
2033
- ...studentTools(canvas),
2034
- ...dashboardTools(canvas)
2035
- ];
2124
+ return toolDomainCatalog.flatMap((registration) => registration.getTools(canvas));
2036
2125
  }
2037
2126
  function registerAllTools(server, canvas) {
2038
2127
  const tools = getAllTools(canvas);
@@ -2177,4 +2266,4 @@ function createCanvasMCPServer(config) {
2177
2266
 
2178
2267
 
2179
2268
  exports.createCanvasMCPServer = createCanvasMCPServer;
2180
- //# sourceMappingURL=chunk-VDHNHHYO.cjs.map
2269
+ //# sourceMappingURL=chunk-2BS2WOAF.cjs.map