canvas-lms-mcp 2.2.0 → 2.3.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 +7 -7
- package/dist/canvas/discussions.d.ts +2 -0
- package/dist/canvas/discussions.d.ts.map +1 -1
- package/dist/canvas/index.cjs +3 -3
- package/dist/canvas/index.js +2 -2
- package/dist/{chunk-FKTJGYLG.js → chunk-24IEEOBF.js} +64 -3
- package/dist/chunk-24IEEOBF.js.map +1 -0
- package/dist/{chunk-JR74DYXY.cjs → chunk-6VFRPUTQ.cjs} +389 -328
- package/dist/chunk-6VFRPUTQ.cjs.map +1 -0
- package/dist/{chunk-FP4A34TD.js → chunk-I4EZ5NSC.js} +12 -2
- package/dist/chunk-I4EZ5NSC.js.map +1 -0
- package/dist/{chunk-C2C3TYWD.cjs → chunk-SVXYCCZ4.cjs} +2 -2
- package/dist/{chunk-C2C3TYWD.cjs.map → chunk-SVXYCCZ4.cjs.map} +1 -1
- package/dist/{chunk-W5DF5YBG.js → chunk-TEYUOD55.js} +2 -2
- package/dist/{chunk-W5DF5YBG.js.map → chunk-TEYUOD55.js.map} +1 -1
- package/dist/{chunk-EMIO4XD4.cjs → chunk-V25MGZFN.cjs} +14 -4
- package/dist/chunk-V25MGZFN.cjs.map +1 -0
- package/dist/http.cjs +6 -6
- package/dist/http.js +3 -3
- package/dist/init.cjs +3 -3
- package/dist/init.js +1 -1
- package/dist/provenance/fields.d.ts.map +1 -1
- package/dist/server.cjs +4 -4
- package/dist/server.js +3 -3
- package/dist/stdio.cjs +4 -4
- package/dist/stdio.js +3 -3
- package/dist/tools/discussions.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-EMIO4XD4.cjs.map +0 -1
- package/dist/chunk-FKTJGYLG.js.map +0 -1
- package/dist/chunk-FP4A34TD.js.map +0 -1
- package/dist/chunk-JR74DYXY.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
MCP server for [Canvas LMS](https://www.instructure.com/canvas). Read courses, assignments, submissions, rubrics, quizzes; grade, comment, manage course content, and handle Canvas admin workflows from any AI agent.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
174 tools across Canvas courses, assignments, submissions, gradebook history, rubrics, quizzes, New Quizzes (LTI), files, users, groups, enrollments, discussions, modules, pages, calendar, conversations, peer reviews, accounts, analytics, outcomes, grading standards, grade projection, link audit, accessibility audit, content exports, content migrations, quiz accommodations, appointment groups, student workflows, student search, dashboard, instructor attention workflows, and health checks. Three deployment modes: stdio, HTTP, and library import.
|
|
15
15
|
|
|
16
16
|
## One-click install (Claude Desktop)
|
|
17
17
|
|
|
@@ -45,7 +45,7 @@ Installs the MCP server (via `npx canvas-lms-mcp`) and all 16 [Agent Skills](#ag
|
|
|
45
45
|
| | canvas-lms-mcp | [vishalsachdev/canvas-mcp](https://github.com/vishalsachdev/canvas-mcp) | [DMontgomery40/mcp-canvas-lms](https://github.com/DMontgomery40/mcp-canvas-lms) |
|
|
46
46
|
|---|---|---|---|
|
|
47
47
|
| Language | TypeScript | Python | TypeScript |
|
|
48
|
-
| Tools |
|
|
48
|
+
| Tools | 174 | 80+ | 54 |
|
|
49
49
|
| License | [](https://github.com/bruchris/canvas-lms-mcp/blob/main/LICENSE) | [](https://github.com/vishalsachdev/canvas-mcp/blob/main/LICENSE) | [](https://github.com/DMontgomery40/mcp-canvas-lms/blob/main/LICENSE) |
|
|
50
50
|
| Last commit | [](https://github.com/bruchris/canvas-lms-mcp) | [](https://github.com/vishalsachdev/canvas-mcp) | [](https://github.com/DMontgomery40/mcp-canvas-lms) |
|
|
51
51
|
|
|
@@ -152,7 +152,7 @@ Once configured, try these prompts with your AI client:
|
|
|
152
152
|
| Attention | `list_submission_comments_needing_attention`, `list_students_needing_attention` |
|
|
153
153
|
| FERPA (conditional) | `resolve_pseudonym` — stdio only, registered when `CANVAS_PSEUDONYMIZE_STUDENTS=true` and `CANVAS_PSEUDONYMIZE_REVERSE_LOOKUP=true` |
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
122 tools are read-only and 52 tools perform Canvas write operations. When FERPA mode is enabled **on the stdio transport**, `resolve_pseudonym` is registered as the 175th tool overall (123rd read tool). The HTTP transport never registers it — see [FERPA mode](#ferpa-mode-student-pseudonymization).
|
|
156
156
|
|
|
157
157
|
All write tools require appropriate Canvas permissions. Canvas enforces its own permission model -- the MCP server does not bypass it.
|
|
158
158
|
|
|
@@ -545,10 +545,10 @@ Three roles, plus the default of "unset = every tool":
|
|
|
545
545
|
|
|
546
546
|
| `CANVAS_ROLE` | Tools exposed | Typical use |
|
|
547
547
|
|---------------|---------------|-------------|
|
|
548
|
-
| _(unset)_ | all (~
|
|
549
|
-
| `student` | ~
|
|
550
|
-
| `teacher` | ~
|
|
551
|
-
| `admin` | ~
|
|
548
|
+
| _(unset)_ | all (~174) | default; backwards-compatible |
|
|
549
|
+
| `student` | ~63 | a student's own courses, grades, submissions, and read-only course content |
|
|
550
|
+
| `teacher` | ~151 | grading, roster, content authoring, analytics |
|
|
551
|
+
| `admin` | ~163 | everything `teacher` sees plus account-level tools (`enroll_user`, `list_account_users`, …) |
|
|
552
552
|
|
|
553
553
|
Notes:
|
|
554
554
|
|
|
@@ -7,6 +7,8 @@ export declare class DiscussionsModule {
|
|
|
7
7
|
list(courseId: CanvasId): Promise<CanvasDiscussionTopic[]>;
|
|
8
8
|
get(courseId: CanvasId, topicId: CanvasId): Promise<CanvasDiscussionTopic>;
|
|
9
9
|
listAnnouncements(courseId: CanvasId): Promise<CanvasAnnouncement[]>;
|
|
10
|
+
listForGroup(groupId: CanvasId): Promise<CanvasDiscussionTopic[]>;
|
|
11
|
+
getForGroup(groupId: CanvasId, topicId: CanvasId): Promise<CanvasDiscussionTopic>;
|
|
10
12
|
postEntry(courseId: CanvasId, topicId: CanvasId, message: string): Promise<CanvasDiscussionEntry>;
|
|
11
13
|
create(courseId: CanvasId, params: CreateDiscussionParams): Promise<CanvasDiscussionTopic>;
|
|
12
14
|
update(courseId: CanvasId, topicId: CanvasId, params: UpdateDiscussionParams): Promise<CanvasDiscussionTopic>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discussions.d.ts","sourceRoot":"","sources":["../../src/canvas/discussions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EACV,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACvB,MAAM,SAAS,CAAA;AAChB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAA;AAEpC,qBAAa,iBAAiB;IAChB,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,gBAAgB;IAEtC,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAM1D,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAM1E,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAOpE,SAAS,CACb,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,qBAAqB,CAAC;IAU3B,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAU1F,MAAM,CACV,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,QAAQ,EACjB,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CAAC,qBAAqB,CAAC;IAU3B,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CAKnE"}
|
|
1
|
+
{"version":3,"file":"discussions.d.ts","sourceRoot":"","sources":["../../src/canvas/discussions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EACV,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACvB,MAAM,SAAS,CAAA;AAChB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAA;AAEpC,qBAAa,iBAAiB;IAChB,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,gBAAgB;IAEtC,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAM1D,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAM1E,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAOpE,YAAY,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAMjE,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAMjF,SAAS,CACb,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,qBAAqB,CAAC;IAU3B,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAU1F,MAAM,CACV,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,QAAQ,EACjB,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CAAC,qBAAqB,CAAC;IAU3B,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CAKnE"}
|
package/dist/canvas/index.cjs
CHANGED
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
var
|
|
9
|
+
var _chunkV25MGZFNcjs = require('../chunk-V25MGZFN.cjs');
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
var
|
|
15
|
+
var _chunkSVXYCCZ4cjs = require('../chunk-SVXYCCZ4.cjs');
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
|
|
@@ -25,5 +25,5 @@ var _chunkC2C3TYWDcjs = require('../chunk-C2C3TYWD.cjs');
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
exports.CANVAS_MAX_ID =
|
|
28
|
+
exports.CANVAS_MAX_ID = _chunkV25MGZFNcjs.CANVAS_MAX_ID; exports.CanvasApiError = _chunkSVXYCCZ4cjs.CanvasApiError; exports.CanvasClient = _chunkV25MGZFNcjs.CanvasClient; exports.CanvasHttpClient = _chunkSVXYCCZ4cjs.CanvasHttpClient; exports.MAX_SAFE_CANVAS_NUMBER_ID = _chunkV25MGZFNcjs.MAX_SAFE_CANVAS_NUMBER_ID; exports.appendCanvasQuery = _chunkSVXYCCZ4cjs.appendCanvasQuery; exports.canvasIdInput = _chunkV25MGZFNcjs.canvasIdInput; exports.canvasIdList = _chunkV25MGZFNcjs.canvasIdList; exports.compareCanvasIds = _chunkV25MGZFNcjs.compareCanvasIds; exports.normalizeCanvasIdInput = _chunkV25MGZFNcjs.normalizeCanvasIdInput; exports.toCanvasQuery = _chunkSVXYCCZ4cjs.toCanvasQuery;
|
|
29
29
|
//# sourceMappingURL=index.cjs.map
|
package/dist/canvas/index.js
CHANGED
|
@@ -6,13 +6,13 @@ import {
|
|
|
6
6
|
canvasIdList,
|
|
7
7
|
compareCanvasIds,
|
|
8
8
|
normalizeCanvasIdInput
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-I4EZ5NSC.js";
|
|
10
10
|
import {
|
|
11
11
|
CanvasApiError,
|
|
12
12
|
CanvasHttpClient,
|
|
13
13
|
appendCanvasQuery,
|
|
14
14
|
toCanvasQuery
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-TEYUOD55.js";
|
|
16
16
|
export {
|
|
17
17
|
CANVAS_MAX_ID,
|
|
18
18
|
CanvasApiError,
|
|
@@ -25,11 +25,11 @@ import {
|
|
|
25
25
|
compareCanvasIds,
|
|
26
26
|
mapWithConcurrency,
|
|
27
27
|
normalizeCanvasIdInput
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-I4EZ5NSC.js";
|
|
29
29
|
import {
|
|
30
30
|
CanvasApiError,
|
|
31
31
|
version
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-TEYUOD55.js";
|
|
33
33
|
|
|
34
34
|
// src/server.ts
|
|
35
35
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -804,6 +804,8 @@ var UNTRUSTED_FIELDS = {
|
|
|
804
804
|
// `post_discussion_entry` / `update_discussion` publish as the operator.
|
|
805
805
|
get_discussion: { message: "discussion message" },
|
|
806
806
|
list_discussions: { message: "discussion message" },
|
|
807
|
+
get_group_discussion: { message: "discussion message" },
|
|
808
|
+
list_group_discussions: { message: "discussion message" },
|
|
807
809
|
// Rank 3 — arbitrary sender; `send_conversation` sends as the operator.
|
|
808
810
|
// `last_message` is Canvas's preview string on the conversation itself;
|
|
809
811
|
// `body` is the full text of each message in the thread.
|
|
@@ -2863,6 +2865,9 @@ function anonymityDescription(topic) {
|
|
|
2863
2865
|
function anonymousTopicMessage(courseId, topicId, description) {
|
|
2864
2866
|
return `Discussion topic ${topicId} exists in course ${courseId} \u2014 list_discussions returns it \u2014 but Canvas blocks the topic-scoped detail REST endpoint for anonymous discussion topics and answers it with HTTP 404. This topic is ${description}. The topic ID is correct; no other ID will work. Read the topic's listed attributes from list_discussions, or open the topic in the Canvas web UI. Replying with post_discussion_entry is unaffected.`;
|
|
2865
2867
|
}
|
|
2868
|
+
function anonymousGroupTopicMessage(groupId, topicId, description) {
|
|
2869
|
+
return `Discussion topic ${topicId} exists in group ${groupId} \u2014 list_group_discussions returns it \u2014 but Canvas blocks the topic-scoped detail REST endpoint for anonymous discussion topics and answers it with HTTP 404. This topic is ${description}. The topic ID is correct; no other ID will work. Read the topic's listed attributes from list_group_discussions, or open the topic in the Canvas web UI.`;
|
|
2870
|
+
}
|
|
2866
2871
|
async function describeAnonymityFromList(canvas, courseId, topicId) {
|
|
2867
2872
|
let topics;
|
|
2868
2873
|
try {
|
|
@@ -2873,6 +2878,16 @@ async function describeAnonymityFromList(canvas, courseId, topicId) {
|
|
|
2873
2878
|
const listed = topics.find((topic) => topic.id === topicId);
|
|
2874
2879
|
return listed ? anonymityDescription(listed) : void 0;
|
|
2875
2880
|
}
|
|
2881
|
+
async function describeGroupAnonymityFromList(canvas, groupId, topicId) {
|
|
2882
|
+
let topics;
|
|
2883
|
+
try {
|
|
2884
|
+
topics = await canvas.discussions.listForGroup(groupId);
|
|
2885
|
+
} catch {
|
|
2886
|
+
return void 0;
|
|
2887
|
+
}
|
|
2888
|
+
const listed = topics.find((topic) => topic.id === topicId);
|
|
2889
|
+
return listed ? anonymityDescription(listed) : void 0;
|
|
2890
|
+
}
|
|
2876
2891
|
function assertAnnouncementHonored(topic, action) {
|
|
2877
2892
|
if (topic.is_announcement) return;
|
|
2878
2893
|
throw new Error(
|
|
@@ -2927,6 +2942,52 @@ function discussionTools(canvas) {
|
|
|
2927
2942
|
}
|
|
2928
2943
|
}
|
|
2929
2944
|
},
|
|
2945
|
+
{
|
|
2946
|
+
name: "list_group_discussions",
|
|
2947
|
+
title: "List Group Discussions",
|
|
2948
|
+
description: "List all discussion topics in a group.",
|
|
2949
|
+
inputSchema: {
|
|
2950
|
+
group_id: canvasIdInput().describe("The Canvas group ID")
|
|
2951
|
+
},
|
|
2952
|
+
annotations: {
|
|
2953
|
+
readOnlyHint: true,
|
|
2954
|
+
openWorldHint: true
|
|
2955
|
+
},
|
|
2956
|
+
handler: async (params) => {
|
|
2957
|
+
const group_id = params.group_id;
|
|
2958
|
+
return canvas.discussions.listForGroup(group_id);
|
|
2959
|
+
}
|
|
2960
|
+
},
|
|
2961
|
+
{
|
|
2962
|
+
name: "get_group_discussion",
|
|
2963
|
+
title: "Get Group Discussion",
|
|
2964
|
+
// As with get_discussion, the anonymous-topic case is reported by the handler's
|
|
2965
|
+
// error below rather than advertised here — `docs/generated/tool-manifest.json`
|
|
2966
|
+
// embeds this string, so any edit also requires `pnpm generate:manifests`.
|
|
2967
|
+
description: "Get details for a single discussion topic in a group by ID.",
|
|
2968
|
+
inputSchema: {
|
|
2969
|
+
group_id: canvasIdInput().describe("The Canvas group ID"),
|
|
2970
|
+
topic_id: canvasIdInput().describe("The Canvas discussion topic ID")
|
|
2971
|
+
},
|
|
2972
|
+
annotations: {
|
|
2973
|
+
readOnlyHint: true,
|
|
2974
|
+
openWorldHint: true
|
|
2975
|
+
},
|
|
2976
|
+
handler: async (params) => {
|
|
2977
|
+
const group_id = params.group_id;
|
|
2978
|
+
const topic_id = params.topic_id;
|
|
2979
|
+
try {
|
|
2980
|
+
return await canvas.discussions.getForGroup(group_id, topic_id);
|
|
2981
|
+
} catch (error) {
|
|
2982
|
+
if (!(error instanceof CanvasApiError) || error.status !== 404) throw error;
|
|
2983
|
+
const description = await describeGroupAnonymityFromList(canvas, group_id, topic_id);
|
|
2984
|
+
if (description === void 0) throw error;
|
|
2985
|
+
throw new Error(anonymousGroupTopicMessage(group_id, topic_id, description), {
|
|
2986
|
+
cause: error
|
|
2987
|
+
});
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
},
|
|
2930
2991
|
{
|
|
2931
2992
|
name: "list_announcements",
|
|
2932
2993
|
title: "List Announcements",
|
|
@@ -10021,4 +10082,4 @@ export {
|
|
|
10021
10082
|
createSharedPseudonymizer,
|
|
10022
10083
|
createCanvasMCPServer
|
|
10023
10084
|
};
|
|
10024
|
-
//# sourceMappingURL=chunk-
|
|
10085
|
+
//# sourceMappingURL=chunk-24IEEOBF.js.map
|