@mtgibbs/canvas-lms-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +539 -0
  3. package/esm/_dnt.shims.d.ts +6 -0
  4. package/esm/_dnt.shims.d.ts.map +1 -0
  5. package/esm/_dnt.shims.js +61 -0
  6. package/esm/agent.d.ts +10 -0
  7. package/esm/agent.d.ts.map +1 -0
  8. package/esm/agent.js +17 -0
  9. package/esm/cli.js +6 -0
  10. package/esm/package.json +3 -0
  11. package/esm/src/api/assignments.d.ts +40 -0
  12. package/esm/src/api/assignments.d.ts.map +1 -0
  13. package/esm/src/api/assignments.js +133 -0
  14. package/esm/src/api/client.d.ts +61 -0
  15. package/esm/src/api/client.d.ts.map +1 -0
  16. package/esm/src/api/client.js +179 -0
  17. package/esm/src/api/courses.d.ts +32 -0
  18. package/esm/src/api/courses.d.ts.map +1 -0
  19. package/esm/src/api/courses.js +85 -0
  20. package/esm/src/api/stats.d.ts +15 -0
  21. package/esm/src/api/stats.d.ts.map +1 -0
  22. package/esm/src/api/stats.js +58 -0
  23. package/esm/src/api/submissions.d.ts +33 -0
  24. package/esm/src/api/submissions.d.ts.map +1 -0
  25. package/esm/src/api/submissions.js +103 -0
  26. package/esm/src/api/users.d.ts +87 -0
  27. package/esm/src/api/users.d.ts.map +1 -0
  28. package/esm/src/api/users.js +139 -0
  29. package/esm/src/mcp/prompts/course-analysis.d.ts +7 -0
  30. package/esm/src/mcp/prompts/course-analysis.d.ts.map +1 -0
  31. package/esm/src/mcp/prompts/course-analysis.js +36 -0
  32. package/esm/src/mcp/prompts/daily-checkin.d.ts +7 -0
  33. package/esm/src/mcp/prompts/daily-checkin.d.ts.map +1 -0
  34. package/esm/src/mcp/prompts/daily-checkin.js +31 -0
  35. package/esm/src/mcp/prompts/grade-recovery.d.ts +7 -0
  36. package/esm/src/mcp/prompts/grade-recovery.d.ts.map +1 -0
  37. package/esm/src/mcp/prompts/grade-recovery.js +35 -0
  38. package/esm/src/mcp/prompts/index.d.ts +15 -0
  39. package/esm/src/mcp/prompts/index.d.ts.map +1 -0
  40. package/esm/src/mcp/prompts/index.js +20 -0
  41. package/esm/src/mcp/prompts/missing-work-audit.d.ts +7 -0
  42. package/esm/src/mcp/prompts/missing-work-audit.d.ts.map +1 -0
  43. package/esm/src/mcp/prompts/missing-work-audit.js +36 -0
  44. package/esm/src/mcp/prompts/week-planning.d.ts +7 -0
  45. package/esm/src/mcp/prompts/week-planning.d.ts.map +1 -0
  46. package/esm/src/mcp/prompts/week-planning.js +34 -0
  47. package/esm/src/mcp/server.d.ts +15 -0
  48. package/esm/src/mcp/server.d.ts.map +1 -0
  49. package/esm/src/mcp/server.js +51 -0
  50. package/esm/src/mcp/tools/get-courses.d.ts +11 -0
  51. package/esm/src/mcp/tools/get-courses.d.ts.map +1 -0
  52. package/esm/src/mcp/tools/get-courses.js +29 -0
  53. package/esm/src/mcp/tools/get-due-this-week.d.ts +13 -0
  54. package/esm/src/mcp/tools/get-due-this-week.d.ts.map +1 -0
  55. package/esm/src/mcp/tools/get-due-this-week.js +75 -0
  56. package/esm/src/mcp/tools/get-missing-assignments.d.ts +12 -0
  57. package/esm/src/mcp/tools/get-missing-assignments.d.ts.map +1 -0
  58. package/esm/src/mcp/tools/get-missing-assignments.js +33 -0
  59. package/esm/src/mcp/tools/get-stats.d.ts +12 -0
  60. package/esm/src/mcp/tools/get-stats.d.ts.map +1 -0
  61. package/esm/src/mcp/tools/get-stats.js +28 -0
  62. package/esm/src/mcp/tools/get-todo.d.ts +13 -0
  63. package/esm/src/mcp/tools/get-todo.d.ts.map +1 -0
  64. package/esm/src/mcp/tools/get-todo.js +55 -0
  65. package/esm/src/mcp/tools/get-unsubmitted-past-due.d.ts +12 -0
  66. package/esm/src/mcp/tools/get-unsubmitted-past-due.d.ts.map +1 -0
  67. package/esm/src/mcp/tools/get-unsubmitted-past-due.js +64 -0
  68. package/esm/src/mcp/tools/get-upcoming-assignments.d.ts +12 -0
  69. package/esm/src/mcp/tools/get-upcoming-assignments.d.ts.map +1 -0
  70. package/esm/src/mcp/tools/get-upcoming-assignments.js +29 -0
  71. package/esm/src/mcp/tools/index.d.ts +18 -0
  72. package/esm/src/mcp/tools/index.d.ts.map +1 -0
  73. package/esm/src/mcp/tools/index.js +26 -0
  74. package/esm/src/mcp/tools/list-assignments.d.ts +13 -0
  75. package/esm/src/mcp/tools/list-assignments.d.ts.map +1 -0
  76. package/esm/src/mcp/tools/list-assignments.js +40 -0
  77. package/esm/src/mcp/types.d.ts +83 -0
  78. package/esm/src/mcp/types.d.ts.map +1 -0
  79. package/esm/src/mcp/types.js +20 -0
  80. package/esm/src/types/canvas.d.ts +288 -0
  81. package/esm/src/types/canvas.d.ts.map +1 -0
  82. package/esm/src/types/canvas.js +5 -0
  83. package/esm/src/utils/config.d.ts +19 -0
  84. package/esm/src/utils/config.d.ts.map +1 -0
  85. package/esm/src/utils/config.js +54 -0
  86. package/esm/src/utils/init.d.ts +9 -0
  87. package/esm/src/utils/init.d.ts.map +1 -0
  88. package/esm/src/utils/init.js +20 -0
  89. package/manifest.json +91 -0
  90. package/package.json +49 -0
@@ -0,0 +1,51 @@
1
+ /**
2
+ * MCP Server setup and configuration
3
+ * Registers all tools and prompts with the server
4
+ */
5
+ import * as dntShim from "../../_dnt.shims.js";
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { tools } from "./tools/index.js";
8
+ import { prompts } from "./prompts/index.js";
9
+ // Read version from deno.json if available
10
+ let version = "1.0.0";
11
+ try {
12
+ const denoJson = JSON.parse(await dntShim.Deno.readTextFile("./deno.json"));
13
+ version = denoJson.version || version;
14
+ }
15
+ catch {
16
+ // Use default version if deno.json not found
17
+ }
18
+ /**
19
+ * Create and configure the MCP server with all tools and prompts
20
+ */
21
+ export function createServer() {
22
+ const server = new McpServer({
23
+ name: "canvas-lms-mcp",
24
+ version,
25
+ });
26
+ // Register all tools with annotations
27
+ for (const tool of tools) {
28
+ if (tool.annotations) {
29
+ server.tool(tool.name, tool.description, tool.schema, tool.annotations, tool.handler);
30
+ }
31
+ else {
32
+ server.tool(tool.name, tool.description, tool.schema, tool.handler);
33
+ }
34
+ }
35
+ // Register all prompts
36
+ for (const prompt of prompts) {
37
+ server.prompt(prompt.name, prompt.description, prompt.arguments, prompt.handler);
38
+ }
39
+ return server;
40
+ }
41
+ /**
42
+ * Get server metadata
43
+ */
44
+ export function getServerInfo() {
45
+ return {
46
+ name: "canvas-lms-mcp",
47
+ version,
48
+ tools: tools.map((t) => t.name),
49
+ prompts: prompts.map((p) => p.name),
50
+ };
51
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Tool: get_courses
3
+ * List all active courses and current grades for the student
4
+ */
5
+ import { z } from "zod";
6
+ import { type ToolDefinition } from "../types.js";
7
+ export declare const schema: {
8
+ student_id: z.ZodOptional<z.ZodString>;
9
+ };
10
+ export declare const getCoursesTool: ToolDefinition<typeof schema>;
11
+ //# sourceMappingURL=get-courses.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-courses.d.ts","sourceRoot":"","sources":["../../../../src/src/mcp/tools/get-courses.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAEhE,eAAO,MAAM,MAAM;;CAElB,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,OAAO,MAAM,CAoBxD,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Tool: get_courses
3
+ * List all active courses and current grades for the student
4
+ */
5
+ import { z } from "zod";
6
+ import { listCoursesWithGrades } from "../../api/courses.js";
7
+ import { jsonResponse } from "../types.js";
8
+ export const schema = {
9
+ student_id: z.string().optional().describe("Student ID (default: 'self')"),
10
+ };
11
+ export const getCoursesTool = {
12
+ name: "get_courses",
13
+ description: "List all active courses and current grades for the student",
14
+ schema,
15
+ annotations: { readOnlyHint: true, openWorldHint: true },
16
+ handler: async ({ student_id }) => {
17
+ const courses = await listCoursesWithGrades(student_id || "self");
18
+ const simplified = courses.map((c) => ({
19
+ id: c.id,
20
+ name: c.name,
21
+ code: c.course_code,
22
+ current_grade: c.enrollment?.grades?.current_grade,
23
+ current_score: c.enrollment?.grades?.current_score,
24
+ final_grade: c.enrollment?.grades?.final_grade,
25
+ final_score: c.enrollment?.grades?.final_score,
26
+ }));
27
+ return jsonResponse(simplified);
28
+ },
29
+ };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Tool: get_due_this_week
3
+ * Get all assignments due in the next N days across ALL courses
4
+ */
5
+ import { z } from "zod";
6
+ import { type ToolDefinition } from "../types.js";
7
+ export declare const schema: {
8
+ student_id: z.ZodString;
9
+ days: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
10
+ hide_graded: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
11
+ };
12
+ export declare const getDueThisWeekTool: ToolDefinition<typeof schema>;
13
+ //# sourceMappingURL=get-due-this-week.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-due-this-week.d.ts","sourceRoot":"","sources":["../../../../src/src/mcp/tools/get-due-this-week.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAEhE,eAAO,MAAM,MAAM;;;;CAQlB,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,OAAO,MAAM,CA2E5D,CAAC"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Tool: get_due_this_week
3
+ * Get all assignments due in the next N days across ALL courses
4
+ */
5
+ import { z } from "zod";
6
+ import { listCourses } from "../../api/courses.js";
7
+ import { listSubmissions } from "../../api/submissions.js";
8
+ import { jsonResponse } from "../types.js";
9
+ export const schema = {
10
+ student_id: z.string().describe("Student ID"),
11
+ days: z.number().optional().default(7).describe("Number of days to look ahead (default: 7)"),
12
+ hide_graded: z
13
+ .boolean()
14
+ .optional()
15
+ .default(true)
16
+ .describe("Hide assignments that have already been graded (default: true)"),
17
+ };
18
+ export const getDueThisWeekTool = {
19
+ name: "get_due_this_week",
20
+ description: "Get all assignments due in the next N days across ALL courses for a student, with submission status",
21
+ schema,
22
+ annotations: { readOnlyHint: true, openWorldHint: true },
23
+ handler: async ({ student_id, days, hide_graded }) => {
24
+ // Get all active courses
25
+ const courses = await listCourses({
26
+ enrollment_state: "active",
27
+ state: ["available"],
28
+ });
29
+ const now = new Date();
30
+ const endDate = new Date(now);
31
+ endDate.setDate(now.getDate() + days);
32
+ const results = [];
33
+ for (const course of courses) {
34
+ try {
35
+ // Get student's submissions with assignment data
36
+ const submissions = await listSubmissions({
37
+ course_id: course.id,
38
+ student_ids: [Number(student_id)],
39
+ include: ["assignment"],
40
+ });
41
+ for (const sub of submissions) {
42
+ const assignment = sub.assignment;
43
+ if (!assignment?.due_at)
44
+ continue;
45
+ const dueDate = new Date(assignment.due_at);
46
+ if (dueDate < now || dueDate > endDate)
47
+ continue;
48
+ results.push({
49
+ course_id: course.id,
50
+ course_name: course.name,
51
+ assignment_id: assignment.id,
52
+ assignment_name: assignment.name,
53
+ due_at: assignment.due_at,
54
+ points_possible: assignment.points_possible,
55
+ submitted: !!sub.submitted_at,
56
+ score: sub.score,
57
+ grade: sub.grade,
58
+ url: assignment.html_url,
59
+ });
60
+ }
61
+ }
62
+ catch {
63
+ // Skip courses we can't access
64
+ }
65
+ }
66
+ // Filter out graded items if requested
67
+ let filteredResults = results;
68
+ if (hide_graded) {
69
+ filteredResults = results.filter((r) => r.score === null);
70
+ }
71
+ // Sort by due date
72
+ filteredResults.sort((a, b) => new Date(a.due_at).getTime() - new Date(b.due_at).getTime());
73
+ return jsonResponse(filteredResults);
74
+ },
75
+ };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Tool: get_missing_assignments
3
+ * Get missing assignments for a student (Canvas-flagged as missing)
4
+ */
5
+ import { z } from "zod";
6
+ import { type ToolDefinition } from "../types.js";
7
+ export declare const schema: {
8
+ student_id: z.ZodOptional<z.ZodString>;
9
+ course_id: z.ZodOptional<z.ZodNumber>;
10
+ };
11
+ export declare const getMissingAssignmentsTool: ToolDefinition<typeof schema>;
12
+ //# sourceMappingURL=get-missing-assignments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-missing-assignments.d.ts","sourceRoot":"","sources":["../../../../src/src/mcp/tools/get-missing-assignments.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAEhE,eAAO,MAAM,MAAM;;;CAGlB,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,cAAc,CAAC,OAAO,MAAM,CAuBnE,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Tool: get_missing_assignments
3
+ * Get missing assignments for a student (Canvas-flagged as missing)
4
+ */
5
+ import { z } from "zod";
6
+ import { getMissingSubmissions } from "../../api/users.js";
7
+ import { jsonResponse } from "../types.js";
8
+ export const schema = {
9
+ student_id: z.string().optional().describe("Student ID (default: 'self')"),
10
+ course_id: z.number().optional().describe("Filter by specific course ID"),
11
+ };
12
+ export const getMissingAssignmentsTool = {
13
+ name: "get_missing_assignments",
14
+ description: "Get missing assignments for a student (Canvas-flagged as missing)",
15
+ schema,
16
+ annotations: { readOnlyHint: true, openWorldHint: true },
17
+ handler: async ({ student_id, course_id }) => {
18
+ const missing = await getMissingSubmissions({
19
+ studentId: student_id || "self",
20
+ courseIds: course_id ? [course_id] : undefined,
21
+ include: ["course"],
22
+ });
23
+ const simplified = missing.map((m) => ({
24
+ id: m.id,
25
+ name: m.name,
26
+ course: m.course?.name,
27
+ due_at: m.due_at,
28
+ points_possible: m.points_possible,
29
+ url: m.html_url,
30
+ }));
31
+ return jsonResponse(simplified);
32
+ },
33
+ };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Tool: get_stats
3
+ * Get late and missing assignment statistics by course
4
+ */
5
+ import { z } from "zod";
6
+ import { type ToolDefinition } from "../types.js";
7
+ export declare const schema: {
8
+ student_id: z.ZodString;
9
+ hide_empty: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
10
+ };
11
+ export declare const getStatsTool: ToolDefinition<typeof schema>;
12
+ //# sourceMappingURL=get-stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-stats.d.ts","sourceRoot":"","sources":["../../../../src/src/mcp/tools/get-stats.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAEhE,eAAO,MAAM,MAAM;;;CAOlB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,OAAO,MAAM,CAetD,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Tool: get_stats
3
+ * Get late and missing assignment statistics by course
4
+ */
5
+ import { z } from "zod";
6
+ import { getStudentStats } from "../../api/stats.js";
7
+ import { jsonResponse } from "../types.js";
8
+ export const schema = {
9
+ student_id: z.string().describe("Student ID"),
10
+ hide_empty: z
11
+ .boolean()
12
+ .optional()
13
+ .default(true)
14
+ .describe("Hide courses with no assignments (default: true)"),
15
+ };
16
+ export const getStatsTool = {
17
+ name: "get_stats",
18
+ description: "Get late and missing assignment statistics by course, showing percentages to identify problem areas",
19
+ schema,
20
+ annotations: { readOnlyHint: true, openWorldHint: true },
21
+ handler: async ({ student_id, hide_empty }) => {
22
+ let stats = await getStudentStats(student_id);
23
+ if (hide_empty) {
24
+ stats = stats.filter((s) => s.total > 0);
25
+ }
26
+ return jsonResponse(stats);
27
+ },
28
+ };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Tool: get_todo
3
+ * Get the student's to-do list (planner items)
4
+ */
5
+ import { z } from "zod";
6
+ import { type ToolDefinition } from "../types.js";
7
+ export declare const schema: {
8
+ student_id: z.ZodString;
9
+ days: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
10
+ hide_submitted: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
11
+ };
12
+ export declare const getTodoTool: ToolDefinition<typeof schema>;
13
+ //# sourceMappingURL=get-todo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-todo.d.ts","sourceRoot":"","sources":["../../../../src/src/mcp/tools/get-todo.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAEhE,eAAO,MAAM,MAAM;;;;CAQlB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,cAAc,CAAC,OAAO,MAAM,CA4CrD,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Tool: get_todo
3
+ * Get the student's to-do list (planner items)
4
+ */
5
+ import { z } from "zod";
6
+ import { getPlannerItems } from "../../api/users.js";
7
+ import { jsonResponse } from "../types.js";
8
+ export const schema = {
9
+ student_id: z.string().describe("Student ID"),
10
+ days: z.number().optional().default(7).describe("Number of days to look ahead (default: 7)"),
11
+ hide_submitted: z
12
+ .boolean()
13
+ .optional()
14
+ .default(false)
15
+ .describe("Hide items that have been submitted"),
16
+ };
17
+ export const getTodoTool = {
18
+ name: "get_todo",
19
+ description: "Get the student's to-do list (planner items) showing upcoming assignments, quizzes, and tasks",
20
+ schema,
21
+ annotations: { readOnlyHint: true, openWorldHint: true },
22
+ handler: async ({ student_id, days, hide_submitted }) => {
23
+ const startDate = new Date().toISOString().split("T")[0];
24
+ const endDate = new Date();
25
+ endDate.setDate(endDate.getDate() + days);
26
+ const endDateStr = endDate.toISOString().split("T")[0];
27
+ const items = await getPlannerItems({
28
+ studentId: student_id,
29
+ startDate,
30
+ endDate: endDateStr,
31
+ });
32
+ // Filter out submitted items if requested
33
+ let filteredItems = items;
34
+ if (hide_submitted) {
35
+ filteredItems = items.filter((item) => !item.submissions?.submitted);
36
+ }
37
+ // Sort by due date
38
+ filteredItems.sort((a, b) => {
39
+ return new Date(a.plannable_date).getTime() - new Date(b.plannable_date).getTime();
40
+ });
41
+ // Simplify for LLM output
42
+ const simplified = filteredItems.map((item) => ({
43
+ course_name: item.context_name,
44
+ title: item.plannable.title,
45
+ type: item.plannable_type,
46
+ due_at: item.plannable_date,
47
+ points_possible: item.plannable.points_possible,
48
+ submitted: item.submissions?.submitted || false,
49
+ missing: item.submissions?.missing || false,
50
+ graded: item.submissions?.graded || false,
51
+ url: item.html_url,
52
+ }));
53
+ return jsonResponse(simplified);
54
+ },
55
+ };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Tool: get_unsubmitted_past_due
3
+ * Get assignments that are past due but not submitted
4
+ */
5
+ import { z } from "zod";
6
+ import { type ToolDefinition } from "../types.js";
7
+ export declare const schema: {
8
+ student_id: z.ZodString;
9
+ course_id: z.ZodOptional<z.ZodNumber>;
10
+ };
11
+ export declare const getUnsubmittedPastDueTool: ToolDefinition<typeof schema>;
12
+ //# sourceMappingURL=get-unsubmitted-past-due.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-unsubmitted-past-due.d.ts","sourceRoot":"","sources":["../../../../src/src/mcp/tools/get-unsubmitted-past-due.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAEhE,eAAO,MAAM,MAAM;;;CAMlB,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,cAAc,CAAC,OAAO,MAAM,CAyDnE,CAAC"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Tool: get_unsubmitted_past_due
3
+ * Get assignments that are past due but not submitted
4
+ */
5
+ import { z } from "zod";
6
+ import { listCourses } from "../../api/courses.js";
7
+ import { listUnsubmittedPastDueForStudent } from "../../api/submissions.js";
8
+ import { jsonResponse } from "../types.js";
9
+ export const schema = {
10
+ student_id: z.string().describe("Student ID (required for this endpoint)"),
11
+ course_id: z
12
+ .number()
13
+ .optional()
14
+ .describe("Filter by specific course ID (if omitted, checks all courses)"),
15
+ };
16
+ export const getUnsubmittedPastDueTool = {
17
+ name: "get_unsubmitted_past_due",
18
+ description: "Get assignments that are past due but not submitted (catches items Canvas hasn't flagged as missing yet)",
19
+ schema,
20
+ annotations: { readOnlyHint: true, openWorldHint: true },
21
+ handler: async ({ student_id, course_id }) => {
22
+ const courseIds = [];
23
+ if (course_id) {
24
+ courseIds.push(course_id);
25
+ }
26
+ else {
27
+ // Get all active courses
28
+ const courses = await listCourses({
29
+ enrollment_state: "active",
30
+ state: ["available"],
31
+ });
32
+ courseIds.push(...courses.map((c) => c.id));
33
+ }
34
+ const results = [];
35
+ for (const cid of courseIds) {
36
+ try {
37
+ const unsubmitted = await listUnsubmittedPastDueForStudent(cid, student_id);
38
+ for (const sub of unsubmitted) {
39
+ const assignment = sub.assignment;
40
+ if (!assignment)
41
+ continue;
42
+ results.push({
43
+ id: assignment.id,
44
+ name: assignment.name,
45
+ course_id: assignment.course_id,
46
+ due_at: assignment.due_at,
47
+ points_possible: assignment.points_possible,
48
+ url: assignment.html_url,
49
+ });
50
+ }
51
+ }
52
+ catch {
53
+ // Skip courses we can't access
54
+ }
55
+ }
56
+ // Sort by due date (most recent first)
57
+ results.sort((a, b) => {
58
+ if (!a.due_at || !b.due_at)
59
+ return 0;
60
+ return new Date(b.due_at).getTime() - new Date(a.due_at).getTime();
61
+ });
62
+ return jsonResponse(results);
63
+ },
64
+ };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Tool: get_upcoming_assignments
3
+ * Get assignments due in the next N days for a single course
4
+ */
5
+ import { z } from "zod";
6
+ import { type ToolDefinition } from "../types.js";
7
+ export declare const schema: {
8
+ course_id: z.ZodNumber;
9
+ days: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
10
+ };
11
+ export declare const getUpcomingAssignmentsTool: ToolDefinition<typeof schema>;
12
+ //# sourceMappingURL=get-upcoming-assignments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-upcoming-assignments.d.ts","sourceRoot":"","sources":["../../../../src/src/mcp/tools/get-upcoming-assignments.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAEhE,eAAO,MAAM,MAAM;;;CAGlB,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,cAAc,CAAC,OAAO,MAAM,CAmBpE,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Tool: get_upcoming_assignments
3
+ * Get assignments due in the next N days for a single course
4
+ */
5
+ import { z } from "zod";
6
+ import { listUpcomingAssignments } from "../../api/assignments.js";
7
+ import { jsonResponse } from "../types.js";
8
+ export const schema = {
9
+ course_id: z.number().describe("Course ID"),
10
+ days: z.number().optional().default(7).describe("Number of days to look ahead (default: 7)"),
11
+ };
12
+ export const getUpcomingAssignmentsTool = {
13
+ name: "get_upcoming_assignments",
14
+ description: "Get assignments due in the next N days for a single course",
15
+ schema,
16
+ annotations: { readOnlyHint: true, openWorldHint: true },
17
+ handler: async ({ course_id, days }) => {
18
+ const assignments = await listUpcomingAssignments(course_id, days);
19
+ const simplified = assignments.map((a) => ({
20
+ id: a.id,
21
+ name: a.name,
22
+ due_at: a.due_at,
23
+ points: a.points_possible,
24
+ submitted: a.submission?.submitted_at ? true : false,
25
+ url: a.html_url,
26
+ }));
27
+ return jsonResponse(simplified);
28
+ },
29
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Tool registry - exports all MCP tools
3
+ */
4
+ import { getCoursesTool } from "./get-courses.js";
5
+ import { getMissingAssignmentsTool } from "./get-missing-assignments.js";
6
+ import { getUnsubmittedPastDueTool } from "./get-unsubmitted-past-due.js";
7
+ import { getUpcomingAssignmentsTool } from "./get-upcoming-assignments.js";
8
+ import { getDueThisWeekTool } from "./get-due-this-week.js";
9
+ import { listAssignmentsTool } from "./list-assignments.js";
10
+ import { getStatsTool } from "./get-stats.js";
11
+ import { getTodoTool } from "./get-todo.js";
12
+ import type { AnyToolDefinition } from "../types.js";
13
+ /**
14
+ * All available MCP tools
15
+ */
16
+ export declare const tools: AnyToolDefinition[];
17
+ export { getCoursesTool, getMissingAssignmentsTool, getUnsubmittedPastDueTool, getUpcomingAssignmentsTool, getDueThisWeekTool, listAssignmentsTool, getStatsTool, getTodoTool, };
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/mcp/tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD;;GAEG;AACH,eAAO,MAAM,KAAK,EAAE,iBAAiB,EASpC,CAAC;AAGF,OAAO,EACL,cAAc,EACd,yBAAyB,EACzB,yBAAyB,EACzB,0BAA0B,EAC1B,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACZ,WAAW,GACZ,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Tool registry - exports all MCP tools
3
+ */
4
+ import { getCoursesTool } from "./get-courses.js";
5
+ import { getMissingAssignmentsTool } from "./get-missing-assignments.js";
6
+ import { getUnsubmittedPastDueTool } from "./get-unsubmitted-past-due.js";
7
+ import { getUpcomingAssignmentsTool } from "./get-upcoming-assignments.js";
8
+ import { getDueThisWeekTool } from "./get-due-this-week.js";
9
+ import { listAssignmentsTool } from "./list-assignments.js";
10
+ import { getStatsTool } from "./get-stats.js";
11
+ import { getTodoTool } from "./get-todo.js";
12
+ /**
13
+ * All available MCP tools
14
+ */
15
+ export const tools = [
16
+ getCoursesTool,
17
+ getMissingAssignmentsTool,
18
+ getUnsubmittedPastDueTool,
19
+ getUpcomingAssignmentsTool,
20
+ getDueThisWeekTool,
21
+ listAssignmentsTool,
22
+ getStatsTool,
23
+ getTodoTool,
24
+ ];
25
+ // Re-export individual tools for direct access if needed
26
+ export { getCoursesTool, getMissingAssignmentsTool, getUnsubmittedPastDueTool, getUpcomingAssignmentsTool, getDueThisWeekTool, listAssignmentsTool, getStatsTool, getTodoTool, };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Tool: list_assignments
3
+ * List assignments for a course with optional filtering
4
+ */
5
+ import { z } from "zod";
6
+ import { type ToolDefinition } from "../types.js";
7
+ export declare const schema: {
8
+ course_id: z.ZodNumber;
9
+ bucket: z.ZodOptional<z.ZodEnum<["past", "overdue", "undated", "ungraded", "unsubmitted", "upcoming", "future"]>>;
10
+ search_term: z.ZodOptional<z.ZodString>;
11
+ };
12
+ export declare const listAssignmentsTool: ToolDefinition<typeof schema>;
13
+ //# sourceMappingURL=list-assignments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-assignments.d.ts","sourceRoot":"","sources":["../../../../src/src/mcp/tools/list-assignments.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAEhE,eAAO,MAAM,MAAM;;;;CAOlB,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,cAAc,CAAC,OAAO,MAAM,CA0B7D,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Tool: list_assignments
3
+ * List assignments for a course with optional filtering
4
+ */
5
+ import { z } from "zod";
6
+ import { listAssignments } from "../../api/assignments.js";
7
+ import { jsonResponse } from "../types.js";
8
+ export const schema = {
9
+ course_id: z.number().describe("Course ID"),
10
+ bucket: z
11
+ .enum(["past", "overdue", "undated", "ungraded", "unsubmitted", "upcoming", "future"])
12
+ .optional()
13
+ .describe("Filter by bucket"),
14
+ search_term: z.string().optional().describe("Search by assignment name"),
15
+ };
16
+ export const listAssignmentsTool = {
17
+ name: "list_assignments",
18
+ description: "List assignments for a course with optional filtering",
19
+ schema,
20
+ annotations: { readOnlyHint: true, openWorldHint: true },
21
+ handler: async ({ course_id, bucket, search_term }) => {
22
+ const assignments = await listAssignments({
23
+ course_id,
24
+ bucket: bucket,
25
+ search_term,
26
+ include: ["submission"],
27
+ });
28
+ const simplified = assignments.map((a) => ({
29
+ id: a.id,
30
+ name: a.name,
31
+ due_at: a.due_at,
32
+ bucket: bucket || "all",
33
+ score: a.submission?.score,
34
+ grade: a.submission?.grade,
35
+ submitted: !!a.submission?.submitted_at,
36
+ url: a.html_url,
37
+ }));
38
+ return jsonResponse(simplified);
39
+ },
40
+ };