@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,269 @@
|
|
|
1
|
+
import { resolveCourseId } from "../common/helpers.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const moduleTools = [
|
|
4
|
+
{
|
|
5
|
+
name: "canvas_list_modules",
|
|
6
|
+
tool: {
|
|
7
|
+
name: "canvas_list_modules",
|
|
8
|
+
description: "List modules and their items 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 modules = await client.getModules(courseId);
|
|
24
|
+
return {
|
|
25
|
+
content: [{ type: "text", text: JSON.stringify(modules, null, 2) }],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "canvas_create_module",
|
|
31
|
+
tool: {
|
|
32
|
+
name: "canvas_create_module",
|
|
33
|
+
description: "Create a new module 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
|
+
name: { type: "string", description: "The name of the module" },
|
|
42
|
+
published: { type: "boolean", description: "Whether the module is published" },
|
|
43
|
+
position: { type: "number", description: "The position of the module" }
|
|
44
|
+
},
|
|
45
|
+
required: ["course_id", "name"],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
handler: async (client, args) => {
|
|
49
|
+
const input = z.object({
|
|
50
|
+
course_id: z.union([z.number(), z.string()]),
|
|
51
|
+
name: z.string(),
|
|
52
|
+
published: z.boolean().optional().default(false),
|
|
53
|
+
position: z.number().optional()
|
|
54
|
+
}).parse(args);
|
|
55
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
56
|
+
const module = await client.createModule(courseId, input.name, input.published, input.position);
|
|
57
|
+
return {
|
|
58
|
+
content: [{ type: "text", text: JSON.stringify(module, null, 2) }],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "canvas_update_module",
|
|
64
|
+
tool: {
|
|
65
|
+
name: "canvas_update_module",
|
|
66
|
+
description: "Update an existing module",
|
|
67
|
+
inputSchema: {
|
|
68
|
+
type: "object",
|
|
69
|
+
properties: {
|
|
70
|
+
course_id: {
|
|
71
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
72
|
+
description: "The ID or name of the course"
|
|
73
|
+
},
|
|
74
|
+
module_id: { type: "number", description: "The ID of the module" },
|
|
75
|
+
name: { type: "string", description: "The new name of the module" },
|
|
76
|
+
published: { type: "boolean", description: "Whether the module is published" },
|
|
77
|
+
position: { type: "number", description: "The new position of the module" }
|
|
78
|
+
},
|
|
79
|
+
required: ["course_id", "module_id"],
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
handler: async (client, args) => {
|
|
83
|
+
const input = z.object({
|
|
84
|
+
course_id: z.union([z.number(), z.string()]),
|
|
85
|
+
module_id: z.number(),
|
|
86
|
+
name: z.string().optional(),
|
|
87
|
+
published: z.boolean().optional(),
|
|
88
|
+
position: z.number().optional()
|
|
89
|
+
}).parse(args);
|
|
90
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
91
|
+
const module = await client.updateModule(courseId, input.module_id, {
|
|
92
|
+
name: input.name,
|
|
93
|
+
published: input.published,
|
|
94
|
+
position: input.position
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
content: [{ type: "text", text: JSON.stringify(module, null, 2) }],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "canvas_delete_module",
|
|
103
|
+
tool: {
|
|
104
|
+
name: "canvas_delete_module",
|
|
105
|
+
description: "Delete a module from a course",
|
|
106
|
+
inputSchema: {
|
|
107
|
+
type: "object",
|
|
108
|
+
properties: {
|
|
109
|
+
course_id: {
|
|
110
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
111
|
+
description: "The ID or name of the course"
|
|
112
|
+
},
|
|
113
|
+
module_id: { type: "number", description: "The ID of the module to delete" },
|
|
114
|
+
},
|
|
115
|
+
required: ["course_id", "module_id"],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
handler: async (client, args) => {
|
|
119
|
+
const input = z.object({
|
|
120
|
+
course_id: z.union([z.number(), z.string()]),
|
|
121
|
+
module_id: z.number()
|
|
122
|
+
}).parse(args);
|
|
123
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
124
|
+
const result = await client.deleteModule(courseId, input.module_id);
|
|
125
|
+
return {
|
|
126
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: "canvas_create_module_item",
|
|
132
|
+
tool: {
|
|
133
|
+
name: "canvas_create_module_item",
|
|
134
|
+
description: "Create a module item (Assignment, Quiz, File, Page, DiscussionTopic, ExternalUrl, ExternalTool, or SubHeader)",
|
|
135
|
+
inputSchema: {
|
|
136
|
+
type: "object",
|
|
137
|
+
properties: {
|
|
138
|
+
course_id: {
|
|
139
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
140
|
+
description: "The ID or name of the course"
|
|
141
|
+
},
|
|
142
|
+
module_id: { type: "number", description: "The ID of the module" },
|
|
143
|
+
type: {
|
|
144
|
+
type: "string",
|
|
145
|
+
description: "The type of item (Assignment, Quiz, File, Page, DiscussionTopic, ExternalUrl, ExternalTool, SubHeader)"
|
|
146
|
+
},
|
|
147
|
+
content_id: {
|
|
148
|
+
anyOf: [{ type: "string" }, { type: "number" }],
|
|
149
|
+
description: "The ID of the content (not needed for SubHeader, ExternalUrl)"
|
|
150
|
+
},
|
|
151
|
+
title: { type: "string", description: "The title of the item" },
|
|
152
|
+
page_url: { type: "string", description: "The URL of the page (for Page type)" },
|
|
153
|
+
external_url: { type: "string", description: "The URL for ExternalUrl or ExternalTool" },
|
|
154
|
+
new_tab: { type: "boolean", description: "Whether to open in a new tab" },
|
|
155
|
+
indent: { type: "number", description: "The level of indentation (0-3)" },
|
|
156
|
+
position: { type: "number", description: "The position in the module" }
|
|
157
|
+
},
|
|
158
|
+
required: ["course_id", "module_id", "type"],
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
handler: async (client, args) => {
|
|
162
|
+
const input = z.object({
|
|
163
|
+
course_id: z.union([z.number(), z.string()]),
|
|
164
|
+
module_id: z.number(),
|
|
165
|
+
type: z.enum(['Assignment', 'Quiz', 'File', 'Page', 'DiscussionTopic', 'ExternalUrl', 'ExternalTool', 'SubHeader']),
|
|
166
|
+
content_id: z.union([z.string(), z.number()]).optional(),
|
|
167
|
+
title: z.string().optional(),
|
|
168
|
+
page_url: z.string().optional(),
|
|
169
|
+
external_url: z.string().optional(),
|
|
170
|
+
new_tab: z.boolean().optional(),
|
|
171
|
+
indent: z.number().optional(),
|
|
172
|
+
position: z.number().optional()
|
|
173
|
+
}).parse(args);
|
|
174
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
175
|
+
const item = {
|
|
176
|
+
type: input.type,
|
|
177
|
+
title: input.title,
|
|
178
|
+
content_id: input.content_id,
|
|
179
|
+
page_url: input.page_url,
|
|
180
|
+
external_url: input.external_url,
|
|
181
|
+
new_tab: input.new_tab,
|
|
182
|
+
indent: input.indent,
|
|
183
|
+
position: input.position
|
|
184
|
+
};
|
|
185
|
+
const result = await client.createModuleItem(courseId, input.module_id, item);
|
|
186
|
+
return {
|
|
187
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: "canvas_update_module_item",
|
|
193
|
+
tool: {
|
|
194
|
+
name: "canvas_update_module_item",
|
|
195
|
+
description: "Update an existing module item",
|
|
196
|
+
inputSchema: {
|
|
197
|
+
type: "object",
|
|
198
|
+
properties: {
|
|
199
|
+
course_id: {
|
|
200
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
201
|
+
description: "The ID or name of the course"
|
|
202
|
+
},
|
|
203
|
+
module_id: { type: "number", description: "The ID of the module" },
|
|
204
|
+
item_id: { type: "number", description: "The ID of the module item" },
|
|
205
|
+
title: { type: "string", description: "The new title of the item" },
|
|
206
|
+
position: { type: "number", description: "The new position of the item" },
|
|
207
|
+
indent: { type: "number", description: "The new level of indentation (0-3)" },
|
|
208
|
+
published: { type: "boolean", description: "Whether the item is published" },
|
|
209
|
+
new_module_id: { type: "number", description: "Move the item to a new module" }
|
|
210
|
+
},
|
|
211
|
+
required: ["course_id", "module_id", "item_id"],
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
handler: async (client, args) => {
|
|
215
|
+
const input = z.object({
|
|
216
|
+
course_id: z.union([z.number(), z.string()]),
|
|
217
|
+
module_id: z.number(),
|
|
218
|
+
item_id: z.number(),
|
|
219
|
+
title: z.string().optional(),
|
|
220
|
+
position: z.number().optional(),
|
|
221
|
+
indent: z.number().optional(),
|
|
222
|
+
published: z.boolean().optional(),
|
|
223
|
+
new_module_id: z.number().optional()
|
|
224
|
+
}).parse(args);
|
|
225
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
226
|
+
const result = await client.updateModuleItem(courseId, input.module_id, input.item_id, {
|
|
227
|
+
title: input.title,
|
|
228
|
+
position: input.position,
|
|
229
|
+
indent: input.indent,
|
|
230
|
+
published: input.published,
|
|
231
|
+
module_id: input.new_module_id
|
|
232
|
+
});
|
|
233
|
+
return {
|
|
234
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: "canvas_delete_module_item",
|
|
240
|
+
tool: {
|
|
241
|
+
name: "canvas_delete_module_item",
|
|
242
|
+
description: "Delete an item from a module",
|
|
243
|
+
inputSchema: {
|
|
244
|
+
type: "object",
|
|
245
|
+
properties: {
|
|
246
|
+
course_id: {
|
|
247
|
+
anyOf: [{ type: "number" }, { type: "string" }],
|
|
248
|
+
description: "The ID or name of the course"
|
|
249
|
+
},
|
|
250
|
+
module_id: { type: "number", description: "The ID of the module" },
|
|
251
|
+
item_id: { type: "number", description: "The ID of the module item to delete" },
|
|
252
|
+
},
|
|
253
|
+
required: ["course_id", "module_id", "item_id"],
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
handler: async (client, args) => {
|
|
257
|
+
const input = z.object({
|
|
258
|
+
course_id: z.union([z.number(), z.string()]),
|
|
259
|
+
module_id: z.number(),
|
|
260
|
+
item_id: z.number()
|
|
261
|
+
}).parse(args);
|
|
262
|
+
const courseId = await resolveCourseId(client, input.course_id);
|
|
263
|
+
const result = await client.deleteModuleItem(courseId, input.module_id, input.item_id);
|
|
264
|
+
return {
|
|
265
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
];
|
|
@@ -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
|
+
];
|