@charlie.act7/canvas-mcp-server 1.2.0 → 1.2.2
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 +262 -113
- package/dist/index.js +29 -7
- package/dist/services/canvas-client.js +193 -7
- package/dist/tools/access-token-tools.js +183 -0
- package/dist/tools/module-tools.js +50 -7
- package/dist/tools/question-bank-tools.js +238 -0
- package/dist/tools/quiz-tools.js +84 -12
- package/package.json +76 -70
- package/.claude-plugin/plugin.json +0 -11
- package/.mcp.json +0 -12
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { resolveCourseId } from "../common/helpers.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const questionBankTools = [
|
|
4
|
+
{
|
|
5
|
+
name: "canvas_list_question_banks",
|
|
6
|
+
tool: {
|
|
7
|
+
name: "canvas_list_question_banks",
|
|
8
|
+
description: "List question banks for a specific course",
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: {
|
|
12
|
+
course_id: {
|
|
13
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
14
|
+
description: "The ID or name of the course"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
required: ["course_id"]
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
handler: async (client, args) => {
|
|
21
|
+
const input = z.object({ course_id: z.union([z.number(), z.string()]) }).parse(args);
|
|
22
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
23
|
+
const banks = await client.listQuestionBanks(courseId);
|
|
24
|
+
return {
|
|
25
|
+
content: [{ type: "text", text: JSON.stringify(banks, null, 2) }]
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "canvas_create_question_bank",
|
|
31
|
+
tool: {
|
|
32
|
+
name: "canvas_create_question_bank",
|
|
33
|
+
description: "Create a new question bank in a course",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: {
|
|
37
|
+
course_id: {
|
|
38
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
39
|
+
description: "The ID or name of the course"
|
|
40
|
+
},
|
|
41
|
+
title: {
|
|
42
|
+
type: "string",
|
|
43
|
+
description: "Title for the question bank"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
required: ["course_id", "title"]
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
handler: async (client, args) => {
|
|
50
|
+
const input = z.object({
|
|
51
|
+
course_id: z.union([z.number(), z.string()]),
|
|
52
|
+
title: z.string().min(1)
|
|
53
|
+
}).parse(args);
|
|
54
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
55
|
+
const bank = await client.createQuestionBank(courseId, input.title);
|
|
56
|
+
return {
|
|
57
|
+
content: [{ type: "text", text: JSON.stringify(bank, null, 2) }]
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "canvas_create_assessment_question",
|
|
63
|
+
tool: {
|
|
64
|
+
name: "canvas_create_assessment_question",
|
|
65
|
+
description: "Create a question in a question bank. Supports types: multiple_choice_question, true_false_question, essay_question, short_answer_question, fill_in_multiple_blanks_question, multiple_answers_question, matching_question, numerical_question.",
|
|
66
|
+
inputSchema: {
|
|
67
|
+
type: "object",
|
|
68
|
+
properties: {
|
|
69
|
+
course_id: {
|
|
70
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
71
|
+
description: "The ID or name of the course"
|
|
72
|
+
},
|
|
73
|
+
bank_id: {
|
|
74
|
+
type: "number",
|
|
75
|
+
description: "The question bank ID"
|
|
76
|
+
},
|
|
77
|
+
name: {
|
|
78
|
+
type: "string",
|
|
79
|
+
description: "Short name/title for the question"
|
|
80
|
+
},
|
|
81
|
+
question_type: {
|
|
82
|
+
type: "string",
|
|
83
|
+
description: "Question type (e.g. multiple_choice_question, true_false_question, essay_question, short_answer_question)"
|
|
84
|
+
},
|
|
85
|
+
question_text: {
|
|
86
|
+
type: "string",
|
|
87
|
+
description: "The question text (HTML supported)"
|
|
88
|
+
},
|
|
89
|
+
points_possible: {
|
|
90
|
+
type: "number",
|
|
91
|
+
description: "Point value for the question"
|
|
92
|
+
},
|
|
93
|
+
answers: {
|
|
94
|
+
type: "array",
|
|
95
|
+
description: "Array of answer objects. For multiple_choice: weight=100 for correct, weight=0 for incorrect. For true_false: text='True'/'False'.",
|
|
96
|
+
items: {
|
|
97
|
+
type: "object",
|
|
98
|
+
properties: {
|
|
99
|
+
text: { type: "string", description: "Answer text" },
|
|
100
|
+
weight: { type: "number", description: "100 for correct, 0 for incorrect" },
|
|
101
|
+
comments: { type: "string", description: "Optional feedback comment" }
|
|
102
|
+
},
|
|
103
|
+
required: ["text", "weight"]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
required: ["course_id", "bank_id", "name", "question_type", "question_text", "points_possible"]
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
handler: async (client, args) => {
|
|
111
|
+
const answerSchema = z.object({
|
|
112
|
+
text: z.string(),
|
|
113
|
+
weight: z.number(),
|
|
114
|
+
comments: z.string().optional()
|
|
115
|
+
});
|
|
116
|
+
const input = z.object({
|
|
117
|
+
course_id: z.union([z.number(), z.string()]),
|
|
118
|
+
bank_id: z.coerce.number(),
|
|
119
|
+
name: z.string().min(1),
|
|
120
|
+
question_type: z.string().min(1),
|
|
121
|
+
question_text: z.string().min(1),
|
|
122
|
+
points_possible: z.coerce.number(),
|
|
123
|
+
answers: z.array(answerSchema).optional()
|
|
124
|
+
}).parse(args);
|
|
125
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
126
|
+
const question = await client.createAssessmentQuestion(courseId, input.bank_id, {
|
|
127
|
+
name: input.name,
|
|
128
|
+
question_type: input.question_type,
|
|
129
|
+
question_text: input.question_text,
|
|
130
|
+
points_possible: input.points_possible,
|
|
131
|
+
answers: input.answers
|
|
132
|
+
});
|
|
133
|
+
return {
|
|
134
|
+
content: [{ type: "text", text: JSON.stringify(question, null, 2) }]
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: "canvas_move_question_bank_questions",
|
|
140
|
+
tool: {
|
|
141
|
+
name: "canvas_move_question_bank_questions",
|
|
142
|
+
description: "Move questions from one question bank to another. If question_ids is omitted, all questions are moved.",
|
|
143
|
+
inputSchema: {
|
|
144
|
+
type: "object",
|
|
145
|
+
properties: {
|
|
146
|
+
course_id: {
|
|
147
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
148
|
+
description: "The ID or name of the course"
|
|
149
|
+
},
|
|
150
|
+
source_bank_id: {
|
|
151
|
+
type: "number",
|
|
152
|
+
description: "Source question bank ID"
|
|
153
|
+
},
|
|
154
|
+
dest_bank_id: {
|
|
155
|
+
type: "number",
|
|
156
|
+
description: "Destination question bank ID"
|
|
157
|
+
},
|
|
158
|
+
question_ids: {
|
|
159
|
+
type: "array",
|
|
160
|
+
items: { type: "number" },
|
|
161
|
+
description: "Optional list of specific question IDs to move. If omitted, all questions are moved."
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
required: ["course_id", "source_bank_id", "dest_bank_id"]
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
handler: async (client, args) => {
|
|
168
|
+
const input = z.object({
|
|
169
|
+
course_id: z.union([z.number(), z.string()]),
|
|
170
|
+
source_bank_id: z.coerce.number(),
|
|
171
|
+
dest_bank_id: z.coerce.number(),
|
|
172
|
+
question_ids: z.array(z.coerce.number()).optional()
|
|
173
|
+
}).parse(args);
|
|
174
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
175
|
+
const result = await client.moveQuestionBankQuestions(courseId, input.source_bank_id, input.dest_bank_id, input.question_ids);
|
|
176
|
+
return {
|
|
177
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: "canvas_create_quiz_group",
|
|
183
|
+
tool: {
|
|
184
|
+
name: "canvas_create_quiz_group",
|
|
185
|
+
description: "Create a quiz group linked to a question bank. The group randomly picks questions from the bank when students take the quiz.",
|
|
186
|
+
inputSchema: {
|
|
187
|
+
type: "object",
|
|
188
|
+
properties: {
|
|
189
|
+
course_id: {
|
|
190
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
191
|
+
description: "The ID or name of the course"
|
|
192
|
+
},
|
|
193
|
+
quiz_id: {
|
|
194
|
+
type: "number",
|
|
195
|
+
description: "The quiz ID"
|
|
196
|
+
},
|
|
197
|
+
name: {
|
|
198
|
+
type: "string",
|
|
199
|
+
description: "Name for the quiz group"
|
|
200
|
+
},
|
|
201
|
+
pick_count: {
|
|
202
|
+
type: "number",
|
|
203
|
+
description: "Number of questions to randomly pick from the bank"
|
|
204
|
+
},
|
|
205
|
+
question_points: {
|
|
206
|
+
type: "number",
|
|
207
|
+
description: "Points per question in this group"
|
|
208
|
+
},
|
|
209
|
+
assessment_question_bank_id: {
|
|
210
|
+
type: "number",
|
|
211
|
+
description: "The question bank ID to link to this group"
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
required: ["course_id", "quiz_id", "name", "pick_count", "question_points"]
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
handler: async (client, args) => {
|
|
218
|
+
const input = z.object({
|
|
219
|
+
course_id: z.union([z.number(), z.string()]),
|
|
220
|
+
quiz_id: z.coerce.number(),
|
|
221
|
+
name: z.string().min(1),
|
|
222
|
+
pick_count: z.coerce.number().min(1),
|
|
223
|
+
question_points: z.coerce.number().min(0),
|
|
224
|
+
assessment_question_bank_id: z.coerce.number().optional()
|
|
225
|
+
}).parse(args);
|
|
226
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
227
|
+
const group = await client.createQuizGroup(courseId, input.quiz_id, {
|
|
228
|
+
name: input.name,
|
|
229
|
+
pick_count: input.pick_count,
|
|
230
|
+
question_points: input.question_points,
|
|
231
|
+
assessment_question_bank_id: input.assessment_question_bank_id
|
|
232
|
+
});
|
|
233
|
+
return {
|
|
234
|
+
content: [{ type: "text", text: JSON.stringify(group, null, 2) }]
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
];
|
package/dist/tools/quiz-tools.js
CHANGED
|
@@ -196,18 +196,18 @@ export const quizTools = [
|
|
|
196
196
|
name: "canvas_create_quiz_question",
|
|
197
197
|
tool: {
|
|
198
198
|
name: "canvas_create_quiz_question",
|
|
199
|
-
description: `Create a new question in a quiz. Supports all 12 Canvas question types:
|
|
200
|
-
- multiple_choice_question: answers with answer_text + answer_weight (100=correct, 0=wrong)
|
|
201
|
-
- true_false_question: two answers ("True"/"False") with answer_weight
|
|
202
|
-
- short_answer_question: one or more correct answer_text values, all with answer_weight 100
|
|
203
|
-
- fill_in_multiple_blanks_question: answers with blank_id + answer_text + answer_weight 100
|
|
204
|
-
- multiple_answers_question: checkboxes; correct answers get answer_weight 100, wrong get 0
|
|
205
|
-
- multiple_dropdowns_question: answers with blank_id + answer_text + answer_weight
|
|
206
|
-
- matching_question: answers with answer_match_left + answer_match_right; add distractors via matching_answer_incorrect_matches on any one answer
|
|
207
|
-
- numerical_question: answers with numerical_answer_type (exact_answer: exact+margin; range_answer: start+end; precision_answer: approximate+precision)
|
|
208
|
-
- calculated_question: answers with variables and formulas (advanced)
|
|
209
|
-
- essay_question: no answers needed
|
|
210
|
-
- file_upload_question: no answers needed
|
|
199
|
+
description: `Create a new question in a quiz. Supports all 12 Canvas question types:
|
|
200
|
+
- multiple_choice_question: answers with answer_text + answer_weight (100=correct, 0=wrong)
|
|
201
|
+
- true_false_question: two answers ("True"/"False") with answer_weight
|
|
202
|
+
- short_answer_question: one or more correct answer_text values, all with answer_weight 100
|
|
203
|
+
- fill_in_multiple_blanks_question: answers with blank_id + answer_text + answer_weight 100
|
|
204
|
+
- multiple_answers_question: checkboxes; correct answers get answer_weight 100, wrong get 0
|
|
205
|
+
- multiple_dropdowns_question: answers with blank_id + answer_text + answer_weight
|
|
206
|
+
- matching_question: answers with answer_match_left + answer_match_right; add distractors via matching_answer_incorrect_matches on any one answer
|
|
207
|
+
- numerical_question: answers with numerical_answer_type (exact_answer: exact+margin; range_answer: start+end; precision_answer: approximate+precision)
|
|
208
|
+
- calculated_question: answers with variables and formulas (advanced)
|
|
209
|
+
- essay_question: no answers needed
|
|
210
|
+
- file_upload_question: no answers needed
|
|
211
211
|
- text_only_question: no answers needed, just displays text`,
|
|
212
212
|
inputSchema: {
|
|
213
213
|
type: "object",
|
|
@@ -437,6 +437,78 @@ export const quizTools = [
|
|
|
437
437
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
438
438
|
}
|
|
439
439
|
},
|
|
440
|
+
{
|
|
441
|
+
name: "canvas_get_quiz_submissions",
|
|
442
|
+
tool: {
|
|
443
|
+
name: "canvas_get_quiz_submissions",
|
|
444
|
+
description: "Get all submissions for a quiz, including user info. Useful to see who has responded.",
|
|
445
|
+
inputSchema: {
|
|
446
|
+
type: "object",
|
|
447
|
+
properties: {
|
|
448
|
+
course_id: { anyOf: [{ type: "number" }, { type: "string" }], description: "The ID or name of the course" },
|
|
449
|
+
quiz_id: { type: "number", description: "The quiz ID" }
|
|
450
|
+
},
|
|
451
|
+
required: ["course_id", "quiz_id"]
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
handler: async (client, args) => {
|
|
455
|
+
const input = z.object({
|
|
456
|
+
course_id: z.union([z.number(), z.string()]),
|
|
457
|
+
quiz_id: z.number()
|
|
458
|
+
}).parse(args);
|
|
459
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
460
|
+
const submissions = await client.getQuizSubmissions(courseId, input.quiz_id);
|
|
461
|
+
return { content: [{ type: "text", text: JSON.stringify(submissions, null, 2) }] };
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: "canvas_check_quiz_pending",
|
|
466
|
+
tool: {
|
|
467
|
+
name: "canvas_check_quiz_pending",
|
|
468
|
+
description: "Check which enrolled students have NOT submitted a quiz. Returns a list of pending students with their name and email. Excludes test accounts.",
|
|
469
|
+
inputSchema: {
|
|
470
|
+
type: "object",
|
|
471
|
+
properties: {
|
|
472
|
+
course_id: { anyOf: [{ type: "number" }, { type: "string" }], description: "The ID or name of the course" },
|
|
473
|
+
quiz_id: { type: "number", description: "The quiz ID" }
|
|
474
|
+
},
|
|
475
|
+
required: ["course_id", "quiz_id"]
|
|
476
|
+
}
|
|
477
|
+
},
|
|
478
|
+
handler: async (client, args) => {
|
|
479
|
+
const input = z.object({
|
|
480
|
+
course_id: z.union([z.number(), z.string()]),
|
|
481
|
+
quiz_id: z.number()
|
|
482
|
+
}).parse(args);
|
|
483
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
484
|
+
const result = await client.checkQuizPending(courseId, input.quiz_id);
|
|
485
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
name: "canvas_delete_quiz",
|
|
490
|
+
tool: {
|
|
491
|
+
name: "canvas_delete_quiz",
|
|
492
|
+
description: "Permanently delete a classic quiz from a course. This cannot be undone.",
|
|
493
|
+
inputSchema: {
|
|
494
|
+
type: "object",
|
|
495
|
+
properties: {
|
|
496
|
+
course_id: { anyOf: [{ type: "number" }, { type: "string" }], description: "The ID or name of the course" },
|
|
497
|
+
quiz_id: { type: "number", description: "The quiz ID to delete" }
|
|
498
|
+
},
|
|
499
|
+
required: ["course_id", "quiz_id"]
|
|
500
|
+
}
|
|
501
|
+
},
|
|
502
|
+
handler: async (client, args) => {
|
|
503
|
+
const input = z.object({
|
|
504
|
+
course_id: z.union([z.number(), z.string()]),
|
|
505
|
+
quiz_id: z.number()
|
|
506
|
+
}).parse(args);
|
|
507
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
508
|
+
const result = await client.deleteQuiz(courseId, input.quiz_id);
|
|
509
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
510
|
+
}
|
|
511
|
+
},
|
|
440
512
|
{
|
|
441
513
|
name: "canvas_update_quiz",
|
|
442
514
|
tool: {
|
package/package.json
CHANGED
|
@@ -1,70 +1,76 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@charlie.act7/canvas-mcp-server",
|
|
3
|
+
"repository": {
|
|
4
|
+
"type": "git",
|
|
5
|
+
"url": "git+https://github.com/CharlieCardenasToledo/mcp-canvas-server.git"
|
|
6
|
+
},
|
|
7
|
+
"homepage": "https://github.com/CharlieCardenasToledo/mcp-canvas-server#readme",
|
|
8
|
+
"bugs": {
|
|
9
|
+
"url": "https://github.com/CharlieCardenasToledo/mcp-canvas-server/issues"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"version": "1.2.2",
|
|
15
|
+
"description": "MCP Server for Canvas LMS - Use AI to interact with your courses, assignments, and grades.",
|
|
16
|
+
"main": "dist/index.js",
|
|
17
|
+
"bin": {
|
|
18
|
+
"canvas-mcp": "dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"files": [
|
|
22
|
+
"dist/index.js",
|
|
23
|
+
"dist/http-server.js",
|
|
24
|
+
"dist/common",
|
|
25
|
+
"dist/prompts",
|
|
26
|
+
"dist/resources",
|
|
27
|
+
"dist/services",
|
|
28
|
+
"dist/tools",
|
|
29
|
+
"README.md",
|
|
30
|
+
"llms-install.md"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc",
|
|
34
|
+
"start": "node dist/index.js",
|
|
35
|
+
"start:http": "node dist/index.js serve-http",
|
|
36
|
+
"dev": "tsc --watch",
|
|
37
|
+
"chat": "tsx src/ollama_bridge.ts",
|
|
38
|
+
"prepublishOnly": "npm run build"
|
|
39
|
+
},
|
|
40
|
+
"keywords": [
|
|
41
|
+
"mcp",
|
|
42
|
+
"canvas",
|
|
43
|
+
"lms",
|
|
44
|
+
"ai",
|
|
45
|
+
"agent",
|
|
46
|
+
"server"
|
|
47
|
+
],
|
|
48
|
+
"author": "Charlie Cárdenas Toledo",
|
|
49
|
+
"license": "MIT",
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@fastify/swagger": "^9.5.2",
|
|
52
|
+
"@fastify/swagger-ui": "^5.2.3",
|
|
53
|
+
"@google/genai": "^2.8.0",
|
|
54
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
55
|
+
"axios": "^1.6.0",
|
|
56
|
+
"chalk": "^5.3.0",
|
|
57
|
+
"commander": "^11.1.0",
|
|
58
|
+
"conf": "^12.0.0",
|
|
59
|
+
"dotenv": "^16.3.1",
|
|
60
|
+
"fastify": "^5.6.1",
|
|
61
|
+
"form-data": "^4.0.5",
|
|
62
|
+
"inquirer": "^9.2.12",
|
|
63
|
+
"ollama": "^0.6.3",
|
|
64
|
+
"zod": "^3.22.4"
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@types/inquirer": "^9.0.7",
|
|
68
|
+
"@types/node": "^20.10.0",
|
|
69
|
+
"nodemon": "^3.1.11",
|
|
70
|
+
"tsx": "^4.21.0",
|
|
71
|
+
"typescript": "^5.3.0"
|
|
72
|
+
},
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": ">=18.0.0"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "canvas-lms",
|
|
3
|
-
"description": "Canvas LMS integration for teachers. Manage courses, assignments, grades, quizzes, students, modules, and more — directly from Claude Code.",
|
|
4
|
-
"version": "1.1.0",
|
|
5
|
-
"author": {
|
|
6
|
-
"name": "CharlieCardenasToledo"
|
|
7
|
-
},
|
|
8
|
-
"homepage": "https://github.com/CharlieCardenasToledo/canvas-mcp-server",
|
|
9
|
-
"repository": "https://github.com/CharlieCardenasToledo/canvas-mcp-server",
|
|
10
|
-
"license": "MIT"
|
|
11
|
-
}
|