From 9aec08246732f7bbfe48c71f4afa8d5bf27977b2 Mon Sep 17 00:00:00 2001 From: Alex Mickelson Date: Mon, 21 Jul 2025 11:42:27 -0600 Subject: [PATCH] working on mcp --- docker-compose.dev.yml | 2 +- .../[transport]/github-classroom-prompt.ts | 79 +++++++++++++++++++ src/app/api/mcp/{ => [transport]}/route.ts | 76 +++++++++++------- 3 files changed, 126 insertions(+), 31 deletions(-) create mode 100644 src/app/api/mcp/[transport]/github-classroom-prompt.ts rename src/app/api/mcp/{ => [transport]}/route.ts (71%) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 0708e93..f8e4ee1 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -47,7 +47,7 @@ services: --api-key "$MCP_TOKEN" \ --server-type "streamable_http" \ --cors-allow-origins "*" \ - -- http://canvas-dev:3000/api/mcp + -- http://canvas-dev:3000/api/mcp/mcp/ ' working_dir: /app ports: diff --git a/src/app/api/mcp/[transport]/github-classroom-prompt.ts b/src/app/api/mcp/[transport]/github-classroom-prompt.ts new file mode 100644 index 0000000..296a808 --- /dev/null +++ b/src/app/api/mcp/[transport]/github-classroom-prompt.ts @@ -0,0 +1,79 @@ +export const githubClassroomUrlPrompt = ` + +## getting github classroom link + +## Preparation +- Always ask for key information if not provided: + - "Which classroom would you like to create an assignment in?" + - "What would you like to name the assignment?" + +## Step-by-Step Process + +1. **Navigate to GitHub Classroom** + \`\`\`javascript + // Navigate to GitHub Classroom + await page.goto('https://classroom.github.com'); + \`\`\` + +2. **Select the Target Classroom** + \`\`\`javascript + // Click on the desired classroom + await page.getByRole('link', { name: 'your-classroom-name' }).click(); + \`\`\` + +3. **Click the "New assignment" button** + \`\`\`javascript + // Click New assignment + await page.getByRole('link', { name: 'New assignment' }).click(); + \`\`\` + +4. **Enter the assignment title** + \`\`\`javascript + // Fill in the assignment title + await page.getByRole('textbox', { name: 'Assignment title' }).fill('your-assignment-name'); + \`\`\` + +5. **Click "Continue" to move to the next step** + \`\`\`javascript + // Click Continue + await page.getByRole('button', { name: 'Continue' }).click(); + \`\`\` + +6. **Configure starter code (optional) and click "Continue" to proceed** + \`\`\`javascript + // Optional: Select a starter code repository if needed + // await page.getByRole('combobox', { name: 'Find a GitHub repository' }).fill('your-repo'); + + // Click Continue + await page.getByRole('button', { name: 'Continue' }).click(); + \`\`\` + +7. **Set up autograding (optional) and click "Create assignment"** + \`\`\`javascript + // Optional: Set up autograding tests if needed + // await page.getByRole('button', { name: 'Add test' }).click(); + + // Click Create assignment + await page.getByRole('button', { name: 'Create assignment' }).click(); + \`\`\` + +8. **Copy the assignment invitation link to share with students** + \`\`\`javascript + // Click Copy assignment invitation link + await page.getByRole('button', { name: 'Copy assignment invitation' }).first().click(); + + // The link is now in clipboard and ready to share with students + \`\`\` + +## Tips and Best Practices +- Always verify with the user if specific settings are needed (like deadlines, visibility, etc.) +- Test the invitation link by opening it in another browser to ensure it works correctly +- Consider optional features like starter code and autograding tests for more complex assignments +- Remember to communicate the invitation link to students via email, LMS, or other channels + +## Common Issues and Solutions +- If authentication is needed, prompt the user to authenticate in their browser +- If a classroom isn't visible, ensure the user has proper permissions +- For starter code issues, verify that the repository is properly set as a template +- If GitHub Classroom is slow, advise patience during high-traffic periods +` \ No newline at end of file diff --git a/src/app/api/mcp/route.ts b/src/app/api/mcp/[transport]/route.ts similarity index 71% rename from src/app/api/mcp/route.ts rename to src/app/api/mcp/[transport]/route.ts index e1d2511..dbd1065 100644 --- a/src/app/api/mcp/route.ts +++ b/src/app/api/mcp/[transport]/route.ts @@ -1,10 +1,10 @@ import { assignmentMarkdownSerializer } from "@/models/local/assignment/utils/assignmentMarkdownSerializer"; -import { LocalCourseSettings } from "@/models/local/localCourseSettings"; import { groupByStartDate } from "@/models/local/utils/timeUtils"; import { fileStorageService } from "@/services/fileStorage/fileStorageService"; import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; import { createMcpHandler } from "mcp-handler"; import { z } from "zod"; +import { githubClassroomUrlPrompt } from "./github-classroom-prompt"; const handler = createMcpHandler( (server) => { @@ -118,42 +118,58 @@ const handler = createMcpHandler( }; } ); - - server.registerResource( - "course_assignment", - new ResourceTemplate( - "canvas:///courses/{courseName}/module/{moduleName}/assignments/{assignmentName}", - { list: undefined } - ), - { - title: "Course Assignment", - description: "Markdown representation of a course assignment", - }, - async (uri, { courseName, moduleName, assignmentName }) => { - if ( - typeof courseName !== "string" || - typeof moduleName !== "string" || - typeof assignmentName !== "string" - ) { - throw new Error( - "courseName, moduleName, and assignmentName must be strings" - ); - } - const assignment = await fileStorageService.assignments.getAssignment( - courseName, - moduleName, - assignmentName - ); + server.tool( + "get_github_classroom_url_instructions", + "gets instructions for creating a GitHub Classroom assignment, call this to get a prompt showing how to create a GitHub Classroom assignment", + {}, + async () => { return { - contents: [ + content: [ { - uri: uri.href, - text: assignment.description, + type: "text", + text: githubClassroomUrlPrompt, }, ], }; } ); + + // resources dont integrate well right now + // server.registerResource( + // "course_assignment", + // new ResourceTemplate( + // "canvas:///courses/{courseName}/module/{moduleName}/assignments/{assignmentName}", + // { list: undefined } + // ), + // { + // title: "Course Assignment", + // description: "Markdown representation of a course assignment", + // }, + // async (uri, { courseName, moduleName, assignmentName }) => { + // if ( + // typeof courseName !== "string" || + // typeof moduleName !== "string" || + // typeof assignmentName !== "string" + // ) { + // throw new Error( + // "courseName, moduleName, and assignmentName must be strings" + // ); + // } + // const assignment = await fileStorageService.assignments.getAssignment( + // courseName, + // moduleName, + // assignmentName + // ); + // return { + // contents: [ + // { + // uri: uri.href, + // text: assignment.description, + // }, + // ], + // }; + // } + // ); }, { serverInfo: {