From 5f4417083a2b9a02c3c2d736bfb8d32b9e722b28 Mon Sep 17 00:00:00 2001 From: Alex Mickelson Date: Wed, 16 Jul 2025 15:52:30 -0600 Subject: [PATCH] got one mcp endpoint --- src/app/CourseList.tsx | 33 ++------------------- src/app/api/mcp/[transport]/route.ts | 43 +++++++++++++++++++--------- src/models/local/utils/timeUtils.ts | 34 +++++++++++++++++++++- 3 files changed, 65 insertions(+), 45 deletions(-) diff --git a/src/app/CourseList.tsx b/src/app/CourseList.tsx index 7aec5fd..2eb6832 100644 --- a/src/app/CourseList.tsx +++ b/src/app/CourseList.tsx @@ -1,38 +1,9 @@ "use client"; import { useLocalCoursesSettingsQuery } from "@/hooks/localCourse/localCoursesHooks"; -import { LocalCourseSettings } from "@/models/local/localCourseSettings"; +import { getDateKey, getTermName, groupByStartDate } from "@/models/local/utils/timeUtils"; import { getCourseUrl } from "@/services/urlUtils"; import Link from "next/link"; -function getDateKey(dateString: string) { - return dateString.split("T")[0]; -} -function groupByStartDate(courses: LocalCourseSettings[]): { - [key: string]: LocalCourseSettings[]; -} { - return courses.reduce( - (acc, course) => { - const { startDate } = course; - const key = getDateKey(startDate); - if (!acc[key]) { - acc[key] = []; - } - acc[key].push(course); - return acc; - }, - {} as { - [key: string]: LocalCourseSettings[]; - } - ); -} - -function getTermName(startDate: string) { - const [year, month, ..._rest] = startDate.split("-"); - if (month < "04") return "Spring " + year; - if (month < "07") return "Summer " + year; - return "Fall " + year; -} - export default function CourseList() { const { data: allSettings } = useLocalCoursesSettingsQuery(); @@ -67,4 +38,4 @@ export default function CourseList() { ))} ); -} +} \ No newline at end of file diff --git a/src/app/api/mcp/[transport]/route.ts b/src/app/api/mcp/[transport]/route.ts index 66e10ae..b94473f 100644 --- a/src/app/api/mcp/[transport]/route.ts +++ b/src/app/api/mcp/[transport]/route.ts @@ -1,30 +1,47 @@ +import { LocalCourseSettings } from "@/models/local/localCourseSettings"; +import { groupByStartDate } from "@/models/local/utils/timeUtils"; +import { fileStorageService } from "@/services/fileStorage/fileStorageService"; import { createMcpHandler } from "mcp-handler"; import { z } from "zod"; + const handler = createMcpHandler( (server) => { server.tool( - "roll_dice", - "Rolls an N-sided die", - { - sides: z.number().int().min(2), - }, - async ({ sides }) => { - const value = 1 + Math.floor(Math.random() * sides); + "get_current_courses", + "gets courses for the current term", + {}, + async () => { + const settingsList = + await fileStorageService.settings.getAllCoursesSettings(); + + const coursesByStartDate = groupByStartDate(settingsList); + + const sortedDates = Object.keys(coursesByStartDate).sort().reverse(); + + const mostRecentStartDate = sortedDates[0]; + + const courseNames = coursesByStartDate[mostRecentStartDate].map( + (settings) => settings.name + ); return { - content: [{ type: "text", text: `🎲 You rolled a ${value}!` }], + content: courseNames.map((name) => ({ + type: "text", + text: name, + })), }; } ); }, { - // Optional server options + serverInfo: { + name: "Canvas Management MCP Server", + version: "2.0.0", + }, }, { - // Optional redis config - redisUrl: process.env.REDIS_URL, - basePath: "/api/mcp", // this needs to match where the [transport] is located. + basePath: "/api/mcp", maxDuration: 60, verboseLogs: true, } ); -export { handler as GET, handler as POST }; \ No newline at end of file +export { handler as GET, handler as POST }; diff --git a/src/models/local/utils/timeUtils.ts b/src/models/local/utils/timeUtils.ts index 76547f4..10821fa 100644 --- a/src/models/local/utils/timeUtils.ts +++ b/src/models/local/utils/timeUtils.ts @@ -1,3 +1,5 @@ +import { LocalCourseSettings } from "../localCourseSettings"; + const _getDateFromAMPM = ( datePart: string, timePart: string, @@ -57,7 +59,8 @@ export const getDateFromString = (value: string): Date | undefined => { } else if (militaryDateRegex.test(value)) { const [datePart, timePart] = value.split(" "); return _getDateFromMilitary(datePart, timePart); - } if (dateOnlyRegex.test(value)) { + } + if (dateOnlyRegex.test(value)) { return _getDateFromDateOnly(value); } else { if (value) console.log("invalid date format", value); @@ -104,3 +107,32 @@ export const dateToMarkdownString = (date: Date) => { export const getDateOnlyMarkdownString = (date: Date) => { return dateToMarkdownString(date).split(" ")[0]; }; + +export function getTermName(startDate: string) { + const [year, month, ..._rest] = startDate.split("-"); + if (month < "04") return "Spring " + year; + if (month < "07") return "Summer " + year; + return "Fall " + year; +} + +export function getDateKey(dateString: string) { + return dateString.split("T")[0]; +} +export function groupByStartDate(courses: LocalCourseSettings[]): { + [key: string]: LocalCourseSettings[]; +} { + return courses.reduce( + (acc, course) => { + const { startDate } = course; + const key = getDateKey(startDate); + if (!acc[key]) { + acc[key] = []; + } + acc[key].push(course); + return acc; + }, + {} as { + [key: string]: LocalCourseSettings[]; + } + ); +}