Skip to main content
Glama
resources.py6.41 kB
"""MCP resources and prompts for Canvas integration.""" from pathlib import Path from typing import Any from mcp.server.fastmcp import FastMCP from ..core.cache import get_course_id from ..core.client import fetch_all_paginated_results, make_canvas_request from ..core.validation import validate_params def register_resources_and_prompts(mcp: FastMCP) -> None: """Register all MCP resources and prompts.""" @mcp.resource( name="course-syllabus", description="Get the syllabus for a specific course", uri="canvas://course/{course_identifier}/syllabus" ) async def get_course_syllabus(course_identifier: str) -> str: """Get the syllabus for a specific course.""" 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 syllabus: {response['error']}" syllabus_body = response.get("syllabus_body", "") if not syllabus_body: return "No syllabus available for this course." return syllabus_body @mcp.resource( name="assignment-description", description="Get the description for a specific assignment", uri="canvas://course/{course_identifier}/assignment/{assignment_id}/description" ) @validate_params async def get_assignment_description(course_identifier: str | int, assignment_id: str | int) -> str: """Get the description for a specific assignment.""" course_id = await get_course_id(course_identifier) # Ensure assignment_id is a string assignment_id_str = str(assignment_id) response = await make_canvas_request( "get", f"/courses/{course_id}/assignments/{assignment_id_str}" ) if "error" in response: return f"Error fetching assignment description: {response['error']}" description = response.get("description", "") if not description: return "No description available for this assignment." return description @mcp.prompt( name="summarize-course", description="Generate a summary of a Canvas course" ) async def summarize_course(course_identifier: str) -> list[dict[str, Any]]: """Generate a summary of a Canvas course.""" course_id = await get_course_id(course_identifier) # Get course details course_response = await make_canvas_request("get", f"/courses/{course_id}") if "error" in course_response: return [{"role": "user", "content": f"Error fetching course: {course_response['error']}"}] # Get assignments assignments_response = await fetch_all_paginated_results(f"/courses/{course_id}/assignments") if isinstance(assignments_response, dict) and "error" in assignments_response: assignments_info = "Error fetching assignments" else: assignments_count = len(assignments_response) from datetime import datetime current_date = datetime.now().isoformat() upcoming_assignments = [ a for a in assignments_response if a.get("due_at") and a.get("due_at") > current_date ] upcoming_count = len(upcoming_assignments) assignments_info = f"{assignments_count} total assignments, {upcoming_count} upcoming" # Get modules modules_response = await fetch_all_paginated_results(f"/courses/{course_id}/modules") if isinstance(modules_response, dict) and "error" in modules_response: modules_info = "Error fetching modules" else: modules_count = len(modules_response) modules_info = f"{modules_count} modules" # Create prompt course_name = course_response.get("name", "Unknown course") course_code = course_response.get("course_code", "No code") return [ {"role": "system", "content": "You are a helpful assistant that summarizes Canvas course information."}, {"role": "user", "content": f""" Please provide a summary of the Canvas course: Course: {course_name} ({course_code}) Code: {course_code} Assignments: {assignments_info} Modules: {modules_info} Summarize the key information about this course and suggest what the user might want to know about it. """} ] @mcp.resource( name="code-api-file", description="Read TypeScript code execution API files", uri="canvas://code-api/{file_path}" ) async def get_code_api_file(file_path: str) -> str: """Get the contents of a TypeScript file from the code execution API. Args: file_path: Path to the TypeScript file relative to code_api directory (e.g., "canvas/grading/bulkGrade.ts") Returns: The TypeScript file contents, or an error message if not found. """ # Get the code_api directory path code_api_dir = Path(__file__).parent.parent / "code_api" # Construct full path full_path = code_api_dir / file_path # Security: Ensure the path is within code_api directory try: full_path = full_path.resolve() code_api_dir = code_api_dir.resolve() if not str(full_path).startswith(str(code_api_dir)): return "❌ Error: Access denied - path outside code_api directory" except Exception: return "❌ Error: Invalid file path" # Check if file exists if not full_path.exists(): return f"❌ Error: File not found: {file_path}" # Check if it's a TypeScript file if full_path.suffix not in ['.ts', '.js', '.mts', '.mjs']: return f"❌ Error: Only TypeScript/JavaScript files are accessible" # Read and return the file try: with open(full_path, 'r', encoding='utf-8') as f: content = f.read() # Add helpful header header = f""" // Code Execution API: {file_path} // Use with execute_typescript tool for token-efficient bulk operations // Import path: './{file_path.replace('.ts', '.js')}' """ return header + content except Exception as e: return f"❌ Error reading file: {str(e)}"

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

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