@mtgibbs/canvas-lms-mcp 0.2.2 → 0.2.8

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.
@@ -17,6 +17,7 @@ export declare function listCourseEnrollments(courseId: number, options?: {
17
17
  type?: string[];
18
18
  state?: string[];
19
19
  userId?: number | string;
20
+ include?: string[];
20
21
  }): Promise<Enrollment[]>;
21
22
  /**
22
23
  * Get enrollment with grades for a specific user in a course
@@ -25,6 +26,9 @@ export declare function getUserEnrollment(courseId: number, userId: number | str
25
26
  /**
26
27
  * List courses with grades for a user (observer or self)
27
28
  * Enriches course data with enrollment/grade information
29
+ *
30
+ * For observer accounts querying a specific student, this fetches
31
+ * the student's enrollment separately to get their actual grades.
28
32
  */
29
33
  export declare function listCoursesWithGrades(userId?: string | number): Promise<(Course & {
30
34
  enrollment?: Enrollment;
@@ -1 +1 @@
1
- {"version":3,"file":"courses.d.ts","sourceRoot":"","sources":["../../../src/src/api/courses.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAEjF;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAmB3E;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG3D;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IACR,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B,GACA,OAAO,CAAC,UAAU,EAAE,CAAC,CAgBvB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GAAG,MAAM,GACtB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAO5B;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,GAAE,MAAM,GAAG,MAAe,GAC/B,OAAO,CAAC,CAAC,MAAM,GAAG;IAAE,UAAU,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC,EAAE,CAAC,CAyBnD"}
1
+ {"version":3,"file":"courses.d.ts","sourceRoot":"","sources":["../../../src/src/api/courses.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAEjF;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAmB3E;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG3D;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IACR,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,GACA,OAAO,CAAC,UAAU,EAAE,CAAC,CAmBvB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GAAG,MAAM,GACtB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAO5B;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,GAAE,MAAM,GAAG,MAAe,GAC/B,OAAO,CAAC,CAAC,MAAM,GAAG;IAAE,UAAU,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC,EAAE,CAAC,CAmDnD"}
@@ -34,7 +34,10 @@ export function getCourse(courseId) {
34
34
  */
35
35
  export function listCourseEnrollments(courseId, options) {
36
36
  const client = getClient();
37
- const params = {};
37
+ const params = {
38
+ // Always include grades when fetching enrollments
39
+ include: options?.include || ["grades"],
40
+ };
38
41
  if (options?.type) {
39
42
  params.type = options.type;
40
43
  }
@@ -59,6 +62,9 @@ export async function getUserEnrollment(courseId, userId) {
59
62
  /**
60
63
  * List courses with grades for a user (observer or self)
61
64
  * Enriches course data with enrollment/grade information
65
+ *
66
+ * For observer accounts querying a specific student, this fetches
67
+ * the student's enrollment separately to get their actual grades.
62
68
  */
63
69
  export async function listCoursesWithGrades(userId = "self") {
64
70
  // Get all active courses with enrollments included
@@ -67,14 +73,35 @@ export async function listCoursesWithGrades(userId = "self") {
67
73
  include: ["enrollments", "term"],
68
74
  state: ["available"],
69
75
  });
70
- // Filter and enrich with the specific user's enrollment data
76
+ // If querying for a specific student (not "self"), we need to fetch
77
+ // the student's enrollment separately since the courses endpoint
78
+ // only returns the observer's enrollment for parent accounts
79
+ const isObserverQuery = userId !== "self" && !isNaN(Number(userId));
80
+ if (isObserverQuery) {
81
+ // Fetch student enrollments with grades in parallel
82
+ const enrollmentPromises = courses.map(async (course) => {
83
+ try {
84
+ const enrollments = await listCourseEnrollments(course.id, {
85
+ userId: Number(userId),
86
+ type: ["StudentEnrollment"],
87
+ });
88
+ return { courseId: course.id, enrollment: enrollments[0] || null };
89
+ }
90
+ catch {
91
+ return { courseId: course.id, enrollment: null };
92
+ }
93
+ });
94
+ const enrollmentResults = await Promise.all(enrollmentPromises);
95
+ const enrollmentMap = new Map(enrollmentResults.map((r) => [r.courseId, r.enrollment]));
96
+ return courses.map((course) => ({
97
+ ...course,
98
+ enrollment: enrollmentMap.get(course.id) || undefined,
99
+ }));
100
+ }
101
+ // For "self" queries, use the enrollment from the courses response
71
102
  const coursesWithGrades = courses.map((course) => {
72
- // Find the enrollment for this user (or observed user)
73
103
  const enrollment = course.enrollments?.find((e) => {
74
- if (userId === "self") {
75
- return e.type === "StudentEnrollment" || e.type === "ObserverEnrollment";
76
- }
77
- return e.user_id === Number(userId) || e.observed_user?.id === Number(userId);
104
+ return e.type === "StudentEnrollment" || e.type === "ObserverEnrollment";
78
105
  });
79
106
  return {
80
107
  ...course,
@@ -1 +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,CASxD,CAAC"}
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;AAGxB,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAEhE,eAAO,MAAM,MAAM;;CAKlB,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,OAAO,MAAM,CAWxD,CAAC"}
@@ -4,17 +4,22 @@
4
4
  */
5
5
  import { z } from "zod";
6
6
  import { getCourses } from "../../services/index.js";
7
+ import { getEffectiveStudentId } from "../../api/users.js";
7
8
  import { jsonResponse } from "../types.js";
8
9
  export const schema = {
9
- student_id: z.string().optional().describe("Student ID (default: 'self')"),
10
+ student_id: z
11
+ .string()
12
+ .optional()
13
+ .describe("Student ID (uses configured CANVAS_STUDENT_ID if not provided)"),
10
14
  };
11
15
  export const getCoursesTool = {
12
16
  name: "get_courses",
13
- description: "List all active courses and current grades for the student",
17
+ description: "List all active courses with current cumulative grades (score percentages). Use this to get an overview of the student's course load and overall academic standing. For observer/parent accounts, this fetches the observed student's grades, not the observer's. Returns course name, code, and current score percentage.",
14
18
  schema,
15
19
  annotations: { readOnlyHint: true, openWorldHint: true },
16
20
  handler: async ({ student_id }) => {
17
- const courses = await getCourses({ studentId: student_id || "self" });
21
+ const effectiveStudentId = await getEffectiveStudentId(student_id);
22
+ const courses = await getCourses({ studentId: String(effectiveStudentId) });
18
23
  return jsonResponse(courses);
19
24
  },
20
25
  };
@@ -24,7 +24,7 @@ export const schema = {
24
24
  };
25
25
  export const getDueThisWeekTool = {
26
26
  name: "get_due_this_week",
27
- description: "Get all assignments due in the next N days across ALL courses for a student, with submission status",
27
+ description: "Get all assignments due in the next N days across ALL courses at once. This is the recommended tool for questions like 'what's due this week?' or 'what homework do I have?'. Returns assignment name, course, due date, points, and whether it's been submitted. By default hides already-graded items to focus on pending work.",
28
28
  schema,
29
29
  annotations: { readOnlyHint: true, openWorldHint: true },
30
30
  handler: async ({ student_id, days, hide_graded }) => {
@@ -1 +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,CAanE,CAAC"}
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;AAGxB,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAEhE,eAAO,MAAM,MAAM;;;CAMlB,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,cAAc,CAAC,OAAO,MAAM,CAenE,CAAC"}
@@ -4,19 +4,24 @@
4
4
  */
5
5
  import { z } from "zod";
6
6
  import { getMissingAssignments } from "../../services/index.js";
7
+ import { getEffectiveStudentId } from "../../api/users.js";
7
8
  import { jsonResponse } from "../types.js";
8
9
  export const schema = {
9
- student_id: z.string().optional().describe("Student ID (default: 'self')"),
10
+ student_id: z
11
+ .string()
12
+ .optional()
13
+ .describe("Student ID (uses configured CANVAS_STUDENT_ID if not provided)"),
10
14
  course_id: z.number().optional().describe("Filter by specific course ID"),
11
15
  };
12
16
  export const getMissingAssignmentsTool = {
13
17
  name: "get_missing_assignments",
14
- description: "Get missing assignments for a student (Canvas-flagged as missing)",
18
+ description: "Get assignments that Canvas has officially flagged as 'missing'. These are past-due assignments where the teacher has marked them missing. NOTE: This may not catch all unsubmitted work - use get_unsubmitted_past_due for assignments that are past due but not yet flagged by the teacher. Use both tools together for a complete picture of outstanding work.",
15
19
  schema,
16
20
  annotations: { readOnlyHint: true, openWorldHint: true },
17
21
  handler: async ({ student_id, course_id }) => {
22
+ const effectiveStudentId = await getEffectiveStudentId(student_id);
18
23
  const missing = await getMissingAssignments({
19
- studentId: student_id || "self",
24
+ studentId: String(effectiveStudentId),
20
25
  courseId: course_id,
21
26
  });
22
27
  return jsonResponse(missing);
@@ -27,7 +27,7 @@ export const schema = {
27
27
  };
28
28
  export const getRecentGradesTool = {
29
29
  name: "get_recent_grades",
30
- description: "Get recently graded assignments with scores. Can filter by date range and minimum grade threshold.",
30
+ description: "Get recently graded assignments with scores from the last N days (default 14). Shows assignment name, course, score, points possible, and percentage. Use below_percentage to filter for low grades (e.g., below_percentage=70 to find grades under 70%). Useful for questions like 'how did I do on recent tests?' or 'any bad grades lately?'",
31
31
  schema,
32
32
  annotations: { readOnlyHint: true, openWorldHint: true },
33
33
  handler: async ({ student_id, days, below_percentage, course_id }) => {
@@ -19,7 +19,7 @@ export const schema = {
19
19
  };
20
20
  export const getStatsTool = {
21
21
  name: "get_stats",
22
- description: "Get late and missing assignment statistics by course, showing percentages to identify problem areas",
22
+ description: "Get late and missing assignment statistics aggregated by course. Shows total assignments, late count, missing count, and percentages for each course. Use this to identify problem courses or answer questions like 'which class has the most missing work?' or 'how many late assignments do I have?'",
23
23
  schema,
24
24
  annotations: { readOnlyHint: true, openWorldHint: true },
25
25
  handler: async ({ student_id, hide_empty }) => {
@@ -20,7 +20,7 @@ export const schema = {
20
20
  };
21
21
  export const getTodoTool = {
22
22
  name: "get_todo",
23
- description: "Get the student's to-do list (planner items) showing upcoming assignments, quizzes, and tasks",
23
+ description: "Get the student's Canvas planner/to-do list showing upcoming assignments, quizzes, discussions, and tasks. This is what the student sees in their Canvas planner. Shows item type, due date, points, and submission status. Use hide_submitted=true to focus on incomplete items only.",
24
24
  schema,
25
25
  annotations: { readOnlyHint: true, openWorldHint: true },
26
26
  handler: async ({ student_id, days, hide_submitted }) => {
@@ -18,7 +18,7 @@ export const schema = {
18
18
  };
19
19
  export const getUnsubmittedPastDueTool = {
20
20
  name: "get_unsubmitted_past_due",
21
- description: "Get assignments that are past due but not submitted (catches items Canvas hasn't flagged as missing yet)",
21
+ description: "Get assignments that are past due but have no submission. This catches work that Canvas hasn't officially flagged as 'missing' yet (teachers may not have updated the status). Use this alongside get_missing_assignments for a complete picture of all outstanding/late work. Returns assignment name, course, due date, and points.",
22
22
  schema,
23
23
  annotations: { readOnlyHint: true, openWorldHint: true },
24
24
  handler: async ({ student_id, course_id }) => {
@@ -1 +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,CAapE,CAAC"}
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,CAcpE,CAAC"}
@@ -11,7 +11,7 @@ export const schema = {
11
11
  };
12
12
  export const getUpcomingAssignmentsTool = {
13
13
  name: "get_upcoming_assignments",
14
- description: "Get assignments due in the next N days for a single course",
14
+ description: "Get assignments due in the next N days for a SINGLE course. Requires course_id. Use this when asking about upcoming work in a specific class. For upcoming assignments across ALL courses at once, use get_due_this_week instead - it's more efficient for broad queries.",
15
15
  schema,
16
16
  annotations: { readOnlyHint: true, openWorldHint: true },
17
17
  handler: async ({ course_id, days }) => {
@@ -1 +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,CAc7D,CAAC"}
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,CAe7D,CAAC"}
@@ -15,7 +15,7 @@ export const schema = {
15
15
  };
16
16
  export const listAssignmentsTool = {
17
17
  name: "list_assignments",
18
- description: "List assignments for a course with optional filtering",
18
+ description: "List assignments for a specific course with optional filtering by bucket (past, overdue, undated, ungraded, unsubmitted, upcoming, future). Use this for detailed assignment queries within a single course, like 'show me all overdue assignments in Math' or 'what assignments are ungraded in Science'. For simpler upcoming/due queries, prefer get_due_this_week.",
19
19
  schema,
20
20
  annotations: { readOnlyHint: true, openWorldHint: true },
21
21
  handler: async ({ course_id, bucket, search_term }) => {
package/manifest.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "manifest_version": "0.3",
3
3
  "name": "canvas-lms-mcp",
4
4
  "display_name": "Canvas LMS",
5
- "version": "0.2.2",
5
+ "version": "0.2.8",
6
6
  "description": "Connect Claude to your Canvas LMS account to query grades, assignments, missing work, and more.",
7
7
  "long_description": "An MCP server that connects AI assistants like Claude to your Canvas LMS account. Perfect for parents monitoring their child's academic progress, students tracking their own work, or anyone who wants to interact with Canvas data through natural language.\n\nAsk Claude things like:\n- What assignments are due this week?\n- Show me missing assignments across all classes\n- What's my grade in Biology?\n- Are there any late assignments I should know about?",
8
8
  "author": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mtgibbs/canvas-lms-mcp",
3
- "version": "0.2.2",
3
+ "version": "0.2.8",
4
4
  "description": "Canvas LMS MCP (Model Context Protocol) server for AI assistants. Query student grades, assignments, and academic data through Claude or other MCP-compatible AI tools.",
5
5
  "keywords": [
6
6
  "mcp",