@charlie.act7/canvas-mcp-server 1.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.
- package/README.md +117 -0
- package/dist/common/config-manager.js +28 -0
- package/dist/common/helpers.js +46 -0
- package/dist/common/tool-model.js +1 -0
- package/dist/common/types.js +1 -0
- package/dist/http-server.js +760 -0
- package/dist/index.js +168 -0
- package/dist/prompts/canvas-prompts.js +64 -0
- package/dist/resources/canvas-resources.js +55 -0
- package/dist/services/canvas-client.js +459 -0
- package/dist/tools/assignment-tools.js +626 -0
- package/dist/tools/calendar-tools.js +240 -0
- package/dist/tools/communication-tools.js +130 -0
- package/dist/tools/config-tools.js +39 -0
- package/dist/tools/course-tools.js +123 -0
- package/dist/tools/create-tools.js +76 -0
- package/dist/tools/file-tools.js +229 -0
- package/dist/tools/grading-tools.js +187 -0
- package/dist/tools/group-tools.js +192 -0
- package/dist/tools/module-tools.js +269 -0
- package/dist/tools/question-bank-tools.js +238 -0
- package/dist/tools/quiz-question-tools.js +303 -0
- package/dist/tools/quiz-tools.js +184 -0
- package/dist/tools/rubric-tools.js +145 -0
- package/dist/tools/student-tools.js +172 -0
- package/package.json +65 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { resolveCourseId, resolveStudentId } from "../common/helpers.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
function pickStudentGradeInfo(student) {
|
|
4
|
+
const enrollment = (student.enrollments || [])[0];
|
|
5
|
+
const grades = enrollment?.grades || {};
|
|
6
|
+
return {
|
|
7
|
+
student_id: student.id,
|
|
8
|
+
student_name: student.name,
|
|
9
|
+
email: student.email,
|
|
10
|
+
current_grade: grades.current_grade ?? null,
|
|
11
|
+
final_grade: grades.final_grade ?? null,
|
|
12
|
+
current_score: grades.current_score ?? null,
|
|
13
|
+
final_score: grades.final_score ?? null
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export const studentTools = [
|
|
17
|
+
{
|
|
18
|
+
name: "canvas_list_students_with_grades",
|
|
19
|
+
tool: {
|
|
20
|
+
name: "canvas_list_students_with_grades",
|
|
21
|
+
description: "List students in a course with their current/final grade and score",
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {
|
|
25
|
+
course_id: {
|
|
26
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
27
|
+
description: "The ID or name of the course"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
required: ["course_id"]
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
handler: async (client, args) => {
|
|
34
|
+
const input = z.object({ course_id: z.union([z.number(), z.string()]) }).parse(args);
|
|
35
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
36
|
+
const students = await client.getEnrollments(courseId);
|
|
37
|
+
const rows = students.map(pickStudentGradeInfo);
|
|
38
|
+
return {
|
|
39
|
+
content: [{ type: "text", text: JSON.stringify(rows, null, 2) }]
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "canvas_get_student_grades",
|
|
45
|
+
tool: {
|
|
46
|
+
name: "canvas_get_student_grades",
|
|
47
|
+
description: "Get grade summary for one student in a course",
|
|
48
|
+
inputSchema: {
|
|
49
|
+
type: "object",
|
|
50
|
+
properties: {
|
|
51
|
+
course_id: {
|
|
52
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
53
|
+
description: "The ID or name of the course"
|
|
54
|
+
},
|
|
55
|
+
student_id: {
|
|
56
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
57
|
+
description: "Student ID or student name"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
required: ["course_id", "student_id"]
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
handler: async (client, args) => {
|
|
64
|
+
const input = z.object({
|
|
65
|
+
course_id: z.union([z.number(), z.string()]),
|
|
66
|
+
student_id: z.union([z.number(), z.string()])
|
|
67
|
+
}).parse(args);
|
|
68
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
69
|
+
const studentId = await resolveStudentId(client, courseId, input.student_id);
|
|
70
|
+
const student = await client.getStudentInCourse(courseId, studentId);
|
|
71
|
+
return {
|
|
72
|
+
content: [{ type: "text", text: JSON.stringify(pickStudentGradeInfo(student), null, 2) }]
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "canvas_get_student_assignments",
|
|
78
|
+
tool: {
|
|
79
|
+
name: "canvas_get_student_assignments",
|
|
80
|
+
description: "Get all assignments for one student with due dates and submission status",
|
|
81
|
+
inputSchema: {
|
|
82
|
+
type: "object",
|
|
83
|
+
properties: {
|
|
84
|
+
course_id: {
|
|
85
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
86
|
+
description: "The ID or name of the course"
|
|
87
|
+
},
|
|
88
|
+
student_id: {
|
|
89
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
90
|
+
description: "Student ID or student name"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
required: ["course_id", "student_id"]
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
handler: async (client, args) => {
|
|
97
|
+
const input = z.object({
|
|
98
|
+
course_id: z.union([z.number(), z.string()]),
|
|
99
|
+
student_id: z.union([z.number(), z.string()])
|
|
100
|
+
}).parse(args);
|
|
101
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
102
|
+
const studentId = await resolveStudentId(client, courseId, input.student_id);
|
|
103
|
+
const submissions = await client.getStudentCourseSubmissions(courseId, studentId);
|
|
104
|
+
const rows = submissions.map((item) => ({
|
|
105
|
+
assignment_id: item.assignment_id,
|
|
106
|
+
assignment_name: item.assignment?.name ?? null,
|
|
107
|
+
due_at: item.assignment?.due_at ?? null,
|
|
108
|
+
unlock_at: item.assignment?.unlock_at ?? null,
|
|
109
|
+
lock_at: item.assignment?.lock_at ?? null,
|
|
110
|
+
submitted_at: item.submitted_at ?? null,
|
|
111
|
+
late: item.late ?? false,
|
|
112
|
+
missing: item.missing ?? false,
|
|
113
|
+
workflow_state: item.workflow_state ?? null,
|
|
114
|
+
grade: item.grade ?? null,
|
|
115
|
+
score: item.score ?? null
|
|
116
|
+
}));
|
|
117
|
+
return {
|
|
118
|
+
content: [{ type: "text", text: JSON.stringify(rows, null, 2) }]
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "canvas_list_assignment_due_dates",
|
|
124
|
+
tool: {
|
|
125
|
+
name: "canvas_list_assignment_due_dates",
|
|
126
|
+
description: "List assignment due dates in a course (optionally only upcoming)",
|
|
127
|
+
inputSchema: {
|
|
128
|
+
type: "object",
|
|
129
|
+
properties: {
|
|
130
|
+
course_id: {
|
|
131
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
132
|
+
description: "The ID or name of the course"
|
|
133
|
+
},
|
|
134
|
+
only_upcoming: {
|
|
135
|
+
type: "boolean",
|
|
136
|
+
description: "If true, return only assignments with due_at >= now"
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
required: ["course_id"]
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
handler: async (client, args) => {
|
|
143
|
+
const input = z.object({
|
|
144
|
+
course_id: z.union([z.number(), z.string()]),
|
|
145
|
+
only_upcoming: z.boolean().optional()
|
|
146
|
+
}).parse(args);
|
|
147
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
148
|
+
const assignments = await client.getAssignments(courseId);
|
|
149
|
+
const now = new Date();
|
|
150
|
+
const rows = assignments
|
|
151
|
+
.map((a) => ({
|
|
152
|
+
assignment_id: a.id ?? null,
|
|
153
|
+
assignment_name: a.name,
|
|
154
|
+
due_at: a.due_at ?? null,
|
|
155
|
+
unlock_at: a.unlock_at ?? null,
|
|
156
|
+
lock_at: a.lock_at ?? null,
|
|
157
|
+
points_possible: a.points_possible ?? null,
|
|
158
|
+
published: a.published ?? null
|
|
159
|
+
}))
|
|
160
|
+
.filter((a) => {
|
|
161
|
+
if (!input.only_upcoming)
|
|
162
|
+
return true;
|
|
163
|
+
if (!a.due_at)
|
|
164
|
+
return false;
|
|
165
|
+
return new Date(a.due_at) >= now;
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
content: [{ type: "text", text: JSON.stringify(rows, null, 2) }]
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
];
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@charlie.act7/canvas-mcp-server",
|
|
3
|
+
"publishConfig": {
|
|
4
|
+
"access": "public"
|
|
5
|
+
},
|
|
6
|
+
"version": "1.1.0",
|
|
7
|
+
"description": "MCP Server for Canvas LMS - Use AI to interact with your courses, assignments, and grades.",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"bin": {
|
|
10
|
+
"canvas-mcp": "dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist/index.js",
|
|
15
|
+
"dist/http-server.js",
|
|
16
|
+
"dist/common",
|
|
17
|
+
"dist/prompts",
|
|
18
|
+
"dist/resources",
|
|
19
|
+
"dist/services",
|
|
20
|
+
"dist/tools",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc",
|
|
25
|
+
"start": "node dist/index.js",
|
|
26
|
+
"start:http": "node dist/index.js serve-http",
|
|
27
|
+
"dev": "tsc --watch",
|
|
28
|
+
"chat": "tsx src/ollama_bridge.ts",
|
|
29
|
+
"prepublishOnly": "npm run build"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"mcp",
|
|
33
|
+
"canvas",
|
|
34
|
+
"lms",
|
|
35
|
+
"ai",
|
|
36
|
+
"agent",
|
|
37
|
+
"server"
|
|
38
|
+
],
|
|
39
|
+
"author": "Charlie Cárdenas Toledo",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@fastify/swagger": "^9.5.2",
|
|
43
|
+
"@fastify/swagger-ui": "^5.2.3",
|
|
44
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
45
|
+
"axios": "^1.6.0",
|
|
46
|
+
"chalk": "^5.3.0",
|
|
47
|
+
"commander": "^11.1.0",
|
|
48
|
+
"conf": "^12.0.0",
|
|
49
|
+
"dotenv": "^16.3.1",
|
|
50
|
+
"fastify": "^5.6.1",
|
|
51
|
+
"form-data": "^4.0.5",
|
|
52
|
+
"inquirer": "^9.2.12",
|
|
53
|
+
"zod": "^3.22.4"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/inquirer": "^9.0.7",
|
|
57
|
+
"@types/node": "^20.10.0",
|
|
58
|
+
"nodemon": "^3.1.11",
|
|
59
|
+
"tsx": "^4.21.0",
|
|
60
|
+
"typescript": "^5.3.0"
|
|
61
|
+
},
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=18.0.0"
|
|
64
|
+
}
|
|
65
|
+
}
|