Skip to main content
Glama

search_assignments

Search for Canvas assignments across courses using keywords, due dates, and course filters to find relevant tasks and deadlines.

Instructions

Searches for assignments across all courses based on title, description, due dates, and course filters.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryNoSearch term to find in assignment titles or descriptions
dueBeforeNoOnly include assignments due before this date (YYYY-MM-DD)
dueAfterNoOnly include assignments due after this date (YYYY-MM-DD)
includeCompletedNoInclude assignments from completed courses
courseIdNoOptional: Limit search to specific course ID

Implementation Reference

  • The core handler logic for the search_assignments tool. Fetches Canvas courses (filtered by courseId or state), retrieves assignments with date filtering via Canvas API, filters by search query in title/description, sorts by due date, and formats a markdown list of matching assignments.
    async ({ query = "", dueBefore, dueAfter, includeCompleted, courseId }) => { try { let courses: CanvasCourse[]; // If courseId is provided, only search that course if (courseId) { courses = [await canvasApiRequest<CanvasCourse>(`/courses/${courseId}`)]; } else { // Otherwise, get all courses based on state const courseState = includeCompleted ? 'all' : 'active'; courses = await canvasApiRequest<CanvasCourse[]>(`/courses?enrollment_state=${courseState}`); } if (courses.length === 0) { return { content: [{ type: "text", text: "No courses found." }] }; } // Search assignments in each course let allResults: AssignmentWithCourse[] = []; for (const course of courses) { try { // Build the assignments query let assignmentsUrl = `/courses/${course.id}/assignments?per_page=100&order_by=due_at&include[]=submission`; // Add date filtering parameters if provided const params = new URLSearchParams(); // Canvas API uses bucket parameter for broad date filtering if (dueAfter && !dueBefore) { params.append('bucket', 'future'); } else if (dueBefore && !dueAfter) { params.append('bucket', 'past'); } // Add specific date range parameters if (dueAfter) { const afterDate = parseDate(dueAfter); if (afterDate) { afterDate.setHours(0, 0, 0, 0); params.append('due_after', afterDate.toISOString()); } } if (dueBefore) { const beforeDate = parseDate(dueBefore); if (beforeDate) { beforeDate.setHours(23, 59, 59, 999); params.append('due_before', beforeDate.toISOString()); } } if (params.toString()) { assignmentsUrl += `&${params.toString()}`; } console.error(`Fetching assignments from URL: ${assignmentsUrl}`); // Debug logging const assignments = await canvasApiRequest<CanvasAssignment[]>(assignmentsUrl); console.error(`Found ${assignments.length} assignments in course ${course.id}`); // Debug logging // Filter by search terms if query is provided const searchTerms = query.toLowerCase().split(/\s+/).filter(term => term.length > 0); const matchingAssignments = searchTerms.length > 0 ? assignments.filter((assignment) => { // Search in title and description const titleMatch = searchTerms.some(term => assignment.name.toLowerCase().includes(term) ); const descriptionMatch = assignment.description ? searchTerms.some(term => htmlToPlainText(assignment.description).toLowerCase().includes(term) ) : false; return titleMatch || descriptionMatch; }) : assignments; // Double-check date range (in case API filter wasn't exact) const dateFilteredAssignments = matchingAssignments.filter(assignment => { // Skip local date filtering if the API is already handling it if ((dueAfter && !dueBefore && params.has('bucket')) || (dueBefore && !dueAfter && params.has('bucket'))) { return true; } return isDateInRange(assignment.due_at, dueBefore, dueAfter); }); // Add course information to each matching assignment dateFilteredAssignments.forEach((assignment) => { allResults.push({ ...assignment, courseName: course.name, courseId: course.id }); }); } catch (error) { console.error(`Error searching in course ${course.id}: ${(error as Error).message}`); // Continue with other courses even if one fails } } // Sort results by due date allResults.sort((a, b) => { // Put assignments with no due date at the end if (!a.due_at && !b.due_at) return 0; if (!a.due_at) return 1; if (!b.due_at) return -1; const dateA = parseDate(a.due_at); const dateB = parseDate(b.due_at); if (!dateA || !dateB) return 0; return dateA.getTime() - dateB.getTime(); }); if (allResults.length === 0) { const dateRange = []; if (dueAfter) dateRange.push(`after ${dueAfter}`); if (dueBefore) dateRange.push(`before ${dueBefore}`); const dateStr = dateRange.length > 0 ? ` due ${dateRange.join(' and ')}` : ''; const queryStr = query ? ` matching "${query}"` : ''; return { content: [{ type: "text", text: `No assignments found${queryStr}${dateStr}.` }] }; } const resultsList = allResults.map((assignment) => { const dueDate = formatDate(assignment.due_at); const status = assignment.published ? '' : ' (Unpublished)'; return [ `- Course: ${assignment.courseName} (ID: ${assignment.courseId})`, ` Assignment: ${assignment.name}${status} (ID: ${assignment.id})`, ` Due: ${dueDate}` ].join('\n'); }).join('\n\n'); const dateRange = []; if (dueAfter) dateRange.push(`after ${dueAfter}`); if (dueBefore) dateRange.push(`before ${dueBefore}`); const dateStr = dateRange.length > 0 ? ` due ${dateRange.join(' and ')}` : ''; const queryStr = query ? ` matching "${query}"` : ''; return { content: [{ type: "text", text: `Found ${allResults.length} assignments${queryStr}${dateStr}:\n\n${resultsList}` }] }; } catch (error) { return { content: [{ type: "text", text: `Search failed: ${(error as Error).message}` }], isError: true }; } }
  • Zod input schema defining parameters for the tool: query (search term), date range filters (dueBefore, dueAfter), includeCompleted (boolean), and optional courseId.
    query: z.string().optional().default("").describe("Search term to find in assignment titles or descriptions"), dueBefore: z.string().optional().describe("Only include assignments due before this date (YYYY-MM-DD)"), dueAfter: z.string().optional().describe("Only include assignments due after this date (YYYY-MM-DD)"), includeCompleted: z.boolean().default(false).describe("Include assignments from completed courses"), courseId: z.string().or(z.number()).optional().describe("Optional: Limit search to specific course ID"), },
  • Registers the 'search_assignments' tool on the MCP server with name, description, input schema, and handler function.
    server.tool( "search_assignments", "Searches for assignments across all courses based on title, description, due dates, and course filters.", { query: z.string().optional().default("").describe("Search term to find in assignment titles or descriptions"), dueBefore: z.string().optional().describe("Only include assignments due before this date (YYYY-MM-DD)"), dueAfter: z.string().optional().describe("Only include assignments due after this date (YYYY-MM-DD)"), includeCompleted: z.boolean().default(false).describe("Include assignments from completed courses"), courseId: z.string().or(z.number()).optional().describe("Optional: Limit search to specific course ID"), }, async ({ query = "", dueBefore, dueAfter, includeCompleted, courseId }) => { try { let courses: CanvasCourse[]; // If courseId is provided, only search that course if (courseId) { courses = [await canvasApiRequest<CanvasCourse>(`/courses/${courseId}`)]; } else { // Otherwise, get all courses based on state const courseState = includeCompleted ? 'all' : 'active'; courses = await canvasApiRequest<CanvasCourse[]>(`/courses?enrollment_state=${courseState}`); } if (courses.length === 0) { return { content: [{ type: "text", text: "No courses found." }] }; } // Search assignments in each course let allResults: AssignmentWithCourse[] = []; for (const course of courses) { try { // Build the assignments query let assignmentsUrl = `/courses/${course.id}/assignments?per_page=100&order_by=due_at&include[]=submission`; // Add date filtering parameters if provided const params = new URLSearchParams(); // Canvas API uses bucket parameter for broad date filtering if (dueAfter && !dueBefore) { params.append('bucket', 'future'); } else if (dueBefore && !dueAfter) { params.append('bucket', 'past'); } // Add specific date range parameters if (dueAfter) { const afterDate = parseDate(dueAfter); if (afterDate) { afterDate.setHours(0, 0, 0, 0); params.append('due_after', afterDate.toISOString()); } } if (dueBefore) { const beforeDate = parseDate(dueBefore); if (beforeDate) { beforeDate.setHours(23, 59, 59, 999); params.append('due_before', beforeDate.toISOString()); } } if (params.toString()) { assignmentsUrl += `&${params.toString()}`; } console.error(`Fetching assignments from URL: ${assignmentsUrl}`); // Debug logging const assignments = await canvasApiRequest<CanvasAssignment[]>(assignmentsUrl); console.error(`Found ${assignments.length} assignments in course ${course.id}`); // Debug logging // Filter by search terms if query is provided const searchTerms = query.toLowerCase().split(/\s+/).filter(term => term.length > 0); const matchingAssignments = searchTerms.length > 0 ? assignments.filter((assignment) => { // Search in title and description const titleMatch = searchTerms.some(term => assignment.name.toLowerCase().includes(term) ); const descriptionMatch = assignment.description ? searchTerms.some(term => htmlToPlainText(assignment.description).toLowerCase().includes(term) ) : false; return titleMatch || descriptionMatch; }) : assignments; // Double-check date range (in case API filter wasn't exact) const dateFilteredAssignments = matchingAssignments.filter(assignment => { // Skip local date filtering if the API is already handling it if ((dueAfter && !dueBefore && params.has('bucket')) || (dueBefore && !dueAfter && params.has('bucket'))) { return true; } return isDateInRange(assignment.due_at, dueBefore, dueAfter); }); // Add course information to each matching assignment dateFilteredAssignments.forEach((assignment) => { allResults.push({ ...assignment, courseName: course.name, courseId: course.id }); }); } catch (error) { console.error(`Error searching in course ${course.id}: ${(error as Error).message}`); // Continue with other courses even if one fails } } // Sort results by due date allResults.sort((a, b) => { // Put assignments with no due date at the end if (!a.due_at && !b.due_at) return 0; if (!a.due_at) return 1; if (!b.due_at) return -1; const dateA = parseDate(a.due_at); const dateB = parseDate(b.due_at); if (!dateA || !dateB) return 0; return dateA.getTime() - dateB.getTime(); }); if (allResults.length === 0) { const dateRange = []; if (dueAfter) dateRange.push(`after ${dueAfter}`); if (dueBefore) dateRange.push(`before ${dueBefore}`); const dateStr = dateRange.length > 0 ? ` due ${dateRange.join(' and ')}` : ''; const queryStr = query ? ` matching "${query}"` : ''; return { content: [{ type: "text", text: `No assignments found${queryStr}${dateStr}.` }] }; } const resultsList = allResults.map((assignment) => { const dueDate = formatDate(assignment.due_at); const status = assignment.published ? '' : ' (Unpublished)'; return [ `- Course: ${assignment.courseName} (ID: ${assignment.courseId})`, ` Assignment: ${assignment.name}${status} (ID: ${assignment.id})`, ` Due: ${dueDate}` ].join('\n'); }).join('\n\n'); const dateRange = []; if (dueAfter) dateRange.push(`after ${dueAfter}`); if (dueBefore) dateRange.push(`before ${dueBefore}`); const dateStr = dateRange.length > 0 ? ` due ${dateRange.join(' and ')}` : ''; const queryStr = query ? ` matching "${query}"` : ''; return { content: [{ type: "text", text: `Found ${allResults.length} assignments${queryStr}${dateStr}:\n\n${resultsList}` }] }; } catch (error) { return { content: [{ type: "text", text: `Search failed: ${(error as Error).message}` }], isError: true }; } } );
  • src/index.ts:26-26 (registration)
    Invokes the tool registration function to add search_assignments to the main MCP server.
    registerSearchAssignmentsTool(server);
  • src/index.ts:6-6 (registration)
    Imports the registration function for the search_assignments tool.
    import { registerSearchAssignmentsTool } from "./tools/search-assignments.js";

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

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