Skip to main content
Glama
server.js6.42 kB
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { ListResourcesRequestSchema, ReadResourceRequestSchema, SearchResourcesRequestSchema, WatchResourcesRequestSchema, ResourceUpdateMessage } from "@modelcontextprotocol/sdk/schema/index.js"; import { CanvasAPI } from '../services/canvasAPI.js'; import { createHandlers } from './handlers.js'; export function createCanvasMCPServer(config) { return new CanvasMCPServer(config); } export function createHandlers(canvasAPI) { const createResourceMetadata = (course) => ({ endDate: course.end_at, students: course.total_students }); const parseResourceURI = (uri) => { const parts = uri.split('/'); return { type: parts[3], courseId: parts[4], subType: parts[5], subId: parts[6] }; }; return { async handleListResources(request) { const courses = await canvasAPI.listCourses(['term', 'total_students']); return { resources: courses.map(course => ({ uri: `canvas:///courses/${course.id}`, name: course.name, metadata: createResourceMetadata(course) })) }; }, async handleReadResource(request) { const { type, courseId, subType, subId } = parseResourceURI(request.params.uri); if (!courseId) { throw new Error('Invalid resource URI: missing course ID'); } let content; switch (type) { case 'courses': { const [course, modules, assignments] = await Promise.all([ canvasAPI.getCourse(courseId, ['term', 'teachers', 'total_students']), canvasAPI.listModules(courseId), canvasAPI.listAssignments(courseId, ['submission']) ]); content = { course, modules, assignments }; break; } case 'assignments': { if (subId && subType === 'submissions') { content = await canvasAPI.listSubmissions(courseId, subId, ['submission_comments']); } else { content = await canvasAPI.listAssignments(courseId, ['submission']); } break; } default: throw new Error(`Unsupported resource type: ${type}`); } return { contents: content }; }, async handleSearchResources(request) { const courses = await canvasAPI.listCourses(['term', 'total_students']); const query = request.params.query.toLowerCase(); const results = courses.filter(course => course.name.toLowerCase().includes(query)); return { resources: results.map(course => ({ uri: `canvas:///courses/${course.id}`, name: course.name, metadata: createResourceMetadata(course) })) }; }, class: CanvasMCPServer }; { canvasAPI: CanvasAPI; server: Server; watchedResources: Map; pollInterval: number = 30000; handlers: ReturnType; constructor(config, CanvasConfig); { this.canvasAPI = new CanvasAPI(config); this.watchedResources = new Map(); this.handlers = createHandlers(this.canvasAPI); this.server = new Server({ name: "canvas-lms-server", version: "1.0.0", }, { capabilities: { resources: { courses: true, modules: true, assignments: true, submissions: true }, search: true, watch: true } }); this.setupRequestHandlers(); this.startWatchPolling(); } setupRequestHandlers(); void { this: .server.setRequestHandler(ListResourcesRequestSchema, this.handlers.handleListResources), this: .server.setRequestHandler(ReadResourceRequestSchema, this.handlers.handleReadResource), this: .server.setRequestHandler(SearchResourcesRequestSchema, this.handlers.handleSearchResources), this: .server.setRequestHandler(WatchResourcesRequestSchema, this.handlers.handleWatchResources) }; async; startWatchPolling(); Promise < void > { setInterval(async) { } }(); { for (const [uri, data] of this.watchedResources.entries()) { try { const newContent = await this.handlers.handleReadResource({ jsonrpc: "2.0", id: "polling", method: "readResource", params: { uri } }); if (JSON.stringify(newContent) !== JSON.stringify(data.lastContent)) { const message = new ResourceUpdateMessage({ uri, contents: newContent.contents }); await this.server.broadcast(message); this.watchedResources.set(uri, { lastCheck: new Date(), lastContent: newContent }); } } catch (error) { console.error(`Error polling resource ${uri}:`, error); } } } this.pollInterval; ; } async; start(); Promise < void > { const: transport = new StdioServerTransport(), await, this: .server.connect(transport), console, : .log('Canvas MCP Server started') }; async; stop(); Promise < void > { // Cleanup watched resources this: .watchedResources.clear(), await, this: .server.disconnect() }; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/DMontgomery40/mcp-canvas-lms'

If you have feedback or need assistance with the MCP directory API, please join our Discord server