Skip to main content
Glama

Canvas MCP Server

"""Course-related MCP tools for Canvas API.""" from typing import Union from mcp.server.fastmcp import FastMCP import sys import os sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from core.client import fetch_all_paginated_results, make_canvas_request from core.cache import get_course_id, get_course_code, course_code_to_id_cache, id_to_course_code_cache from core.validation import validate_params from core.dates import format_date def register_course_tools(mcp: FastMCP): """Register all course-related MCP tools.""" @mcp.tool() async def list_courses(include_concluded: bool = False, include_all: bool = False) -> str: """List courses for the authenticated user.""" params = { "include[]": ["term", "teachers", "total_students"], "per_page": 100 } if not include_all: params["enrollment_type"] = "teacher" if include_concluded: params["state[]"] = ["available", "completed"] else: params["state[]"] = ["available"] courses = await fetch_all_paginated_results("/courses", params) if isinstance(courses, dict) and "error" in courses: return f"Error fetching courses: {courses['error']}" if not courses: return "No courses found." # Refresh our caches with the course data for course in courses: course_id = str(course.get("id")) course_code = course.get("course_code") if course_code and course_id: course_code_to_id_cache[course_code] = course_id id_to_course_code_cache[course_id] = course_code courses_info = [] for course in courses: course_id = course.get("id") name = course.get("name", "Unnamed course") code = course.get("course_code", "No code") # Emphasize code in the output courses_info.append(f"Code: {code}\nName: {name}\nID: {course_id}\n") return "Courses:\n\n" + "\n".join(courses_info) @mcp.tool() @validate_params async def get_course_details(course_identifier: Union[str, int]) -> str: """Get detailed information about a specific course. Args: course_identifier: The Canvas course code (e.g., badm_554_120251_246794) or ID """ course_id = await get_course_id(course_identifier) response = await make_canvas_request("get", f"/courses/{course_id}") if "error" in response: return f"Error fetching course details: {response['error']}" # Update our caches with the course data if "id" in response and "course_code" in response: course_code_to_id_cache[response["course_code"]] = str(response["id"]) id_to_course_code_cache[str(response["id"])] = response["course_code"] details = [ f"Code: {response.get('course_code', 'N/A')}", f"Name: {response.get('name', 'N/A')}", f"Start Date: {format_date(response.get('start_at'))}", f"End Date: {format_date(response.get('end_at'))}", f"Time Zone: {response.get('time_zone', 'N/A')}", f"Default View: {response.get('default_view', 'N/A')}", f"Public: {response.get('is_public', False)}", f"Blueprint: {response.get('blueprint', False)}" ] # Prefer to show course code in the output course_display = response.get("course_code", course_identifier) return f"Course Details for {course_display}:\n\n" + "\n".join(details) @mcp.tool() @validate_params async def get_course_content_overview(course_identifier: Union[str, int], include_pages: bool = True, include_modules: bool = True) -> str: """Get a comprehensive overview of course content including pages and modules. Args: course_identifier: The Canvas course code (e.g., badm_554_120251_246794) or ID include_pages: Whether to include pages information include_modules: Whether to include modules and their items """ course_id = await get_course_id(course_identifier) overview_sections = [] # Get course details for context course_response = await make_canvas_request("get", f"/courses/{course_id}") if "error" not in course_response: course_name = course_response.get("name", "Unknown Course") overview_sections.append(f"Course: {course_name}") # Get pages if requested if include_pages: pages = await fetch_all_paginated_results(f"/courses/{course_id}/pages", {"per_page": 100}) if isinstance(pages, list): published_pages = [p for p in pages if p.get("published", False)] unpublished_pages = [p for p in pages if not p.get("published", False)] front_pages = [p for p in pages if p.get("front_page", False)] pages_summary = [ f"\nPages Summary:", f" Total Pages: {len(pages)}", f" Published: {len(published_pages)}", f" Unpublished: {len(unpublished_pages)}", f" Front Pages: {len(front_pages)}" ] if published_pages: pages_summary.append(f"\nRecent Published Pages:") # Sort by updated_at and show first 5 sorted_pages = sorted(published_pages, key=lambda x: x.get("updated_at", ""), reverse=True) for page in sorted_pages[:5]: title = page.get("title", "Untitled") updated = format_date(page.get("updated_at")) pages_summary.append(f" {title} (Updated: {updated})") overview_sections.append("\n".join(pages_summary)) # Get modules if requested if include_modules: modules = await fetch_all_paginated_results(f"/courses/{course_id}/modules", {"per_page": 100}) if isinstance(modules, list): modules_summary = [ f"\nModules Summary:", f" Total Modules: {len(modules)}" ] # Count module items by type across all modules item_type_counts = {} total_items = 0 for module in modules[:10]: # Limit to first 10 modules to avoid too many API calls module_id = module.get("id") if module_id: items = await fetch_all_paginated_results( f"/courses/{course_id}/modules/{module_id}/items", {"per_page": 100} ) if isinstance(items, list): total_items += len(items) for item in items: item_type = item.get("type", "Unknown") item_type_counts[item_type] = item_type_counts.get(item_type, 0) + 1 modules_summary.append(f" Total Items Analyzed: {total_items}") if item_type_counts: modules_summary.append(f" Item Types:") for item_type, count in sorted(item_type_counts.items()): modules_summary.append(f" {item_type}: {count}") # Show module structure for first few modules if modules: modules_summary.append(f"\nModule Structure (first 3):") for module in modules[:3]: name = module.get("name", "Unnamed") state = module.get("state", "unknown") modules_summary.append(f" {name} (Status: {state})") overview_sections.append("\n".join(modules_summary)) # Try to get the course code for display course_display = await get_course_code(course_id) or course_identifier result = f"Content Overview for Course {course_display}:" + "\n".join(overview_sections) return result

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/vishalsachdev/canvas-mcp'

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