From 40aac4d0dc3f02eadf4130425194213cfa8fbe51 Mon Sep 17 00:00:00 2001 From: Aleksander Fidelus <63016446+FidelusAleksander@users.noreply.github.com> Date: Thu, 27 Mar 2025 09:51:11 +0000 Subject: [PATCH] feat: initial draft of the exercise --- .devcontainer/devcontainer.json | 21 ++++ .github/steps/1-step.md | 72 +++++++++++ .github/steps/2-step.md | 60 +++++++++ .github/steps/3-step.md | 29 +++++ .github/steps/4-step.md | 24 ++++ .github/steps/x-review.md | 18 +++ .github/workflows/0-start-exercise.yml | 74 +++++++++++ .github/workflows/1-step.yml | 166 +++++++++++++++++++++++++ .github/workflows/2-step.yml | 117 +++++++++++++++++ .github/workflows/3-step.yml | 106 ++++++++++++++++ .github/workflows/4-step.yml | 88 +++++++++++++ .gitignore | 41 ++++++ .vscode/launch.json | 19 +++ README.md | 62 +++++++++ requirements.txt | 2 + src/README.md | 50 ++++++++ src/app.py | 67 ++++++++++ src/static/app.js | 86 +++++++++++++ src/static/index.html | 50 ++++++++ src/static/styles.css | 144 +++++++++++++++++++++ 20 files changed, 1296 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .github/steps/1-step.md create mode 100644 .github/steps/2-step.md create mode 100644 .github/steps/3-step.md create mode 100644 .github/steps/4-step.md create mode 100644 .github/steps/x-review.md create mode 100644 .github/workflows/0-start-exercise.yml create mode 100644 .github/workflows/1-step.yml create mode 100644 .github/workflows/2-step.yml create mode 100644 .github/workflows/3-step.yml create mode 100644 .github/workflows/4-step.yml create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 README.md create mode 100644 requirements.txt create mode 100644 src/README.md create mode 100644 src/app.py create mode 100644 src/static/app.js create mode 100644 src/static/index.html create mode 100644 src/static/styles.css diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..608b221 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,21 @@ +{ + "name": "Python 3", + "image": "mcr.microsoft.com/vscode/devcontainers/python:3.13", + "features": { + "ghcr.io/devcontainers/features/node:1": { + "version": "latest" + } + }, + "forwardPorts": [8000], + "postCreateCommand": "pip install -r requirements.txt", + "customizations": { + "vscode": { + "extensions": [ + "GitHub.copilot", + "ms-python.python", + "ms-python.debugpy", + "dbaeumer.vscode-eslint" + ] + } + } +} diff --git a/.github/steps/1-step.md b/.github/steps/1-step.md new file mode 100644 index 0000000..a415c4d --- /dev/null +++ b/.github/steps/1-step.md @@ -0,0 +1,72 @@ +## Step 1: Introduction to MCP and environment setup + +Welcome to the **"Integrate Model Context Protocol with GitHub Copilot"** exercise! :robot: + +In this exercise, you'll learn how Model Context Protocol (MCP) enhances the way you use GitHub Copilot. + +> [!IMPORTANT] +> This exercise may not explain the basics that were taught in the [Getting Started with Copilot](https://github.com/skills/getting-started-with-github-copilot) exercise. If you are new to Copilot we recommend starting with that one. + + +### What is Model Context Protocol (MCP)? + +[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open standard that bridges AI models with external data sources and tools. It provides a universal interface for connecting AI assistants like GitHub Copilot to various system allowing them to access real-time data, perform actions in external systems, and leverage specialized tools beyond their built-in capabilities. +### :keyboard: Activity: Get to know your environment + +Let's start up our development environment and familiarize with the environment. + +We are using the same web application as in the [Getting Started with Copilot](https://github.com/skills/getting-started-with-github-copilot) exercise - the Mergington High School's extracurricular activities website. + +1. Right-click the below button to open the **Create Codespace** page in a new tab. Use the default configuration. + + [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/{{full_repo_name}}?quickstart=1) + +1. Validate the Copilot Chat extension is installed +1. In the left sidebar, select the `Run and Debug` tab and then press the **Start Debugging** icon. + + image + +1. Throughout the exercise, you can access the website link from the `ports` tab on port `8000`. + +### :keyboard: Activity: Set up a MCP server for your project + +1. Inside your codespace, create a new file named `mcp.json` in the `.vscode` directory and paste the following contents: + +```json +// .vscode/mcp.json +{ + "servers": { + "github": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-github"], + "envFile": "${workspaceFolder}/.env" + } + } +} +``` + +1. Save the file and you should see `Start` button show up like so: + + + +1. Validate the MCP Server is running. + 1. The `.vscode/mcp.json` file should show if the server you started is running + + 1. You should see additional tools available in Copilot Agent Mode + + 1. You can use the VSCode command palette `Ctrl+Shift+P` or `Command+Shift+P` on Mac. + Start typing `> MCP` to see different MCP commands, such as listing active servers. + + +1. Commit and push the `.vscode/mcp.json` file to the `main` branch + + +
+Having trouble?
+ +Make sure you: + +- Properly uncommented the contents of `.vscode/mcp.json` file +- Pushed your changes to the `main` branch + +
diff --git a/.github/steps/2-step.md b/.github/steps/2-step.md new file mode 100644 index 0000000..02416e3 --- /dev/null +++ b/.github/steps/2-step.md @@ -0,0 +1,60 @@ +## Step 2: Using Copilot Agent Mode with GitHub MCP Server + +You have just connected a first external MCP server to Copilot Chat! + +GitHub MCP Server exposes multiple tools for Copilot to use. Examples include searching GitHub, managing repositories, issues, pull requests and much more. + +You interact with Copilot in natural language, and Copilot determines if what you are asking for is something that can be done through any MCP Servers you have currently running in your environment. Isn't that cool? + +> [!NOTE] +> The GitHub MCP Server is limited to operations permitted by the `GITHUB_TOKEN` scope in your `.vscode/mcp.json` configuration. + +### :keyboard: Activity: Use Copilot for issue resolution + +In the background your repository just received a [bug report]({{{bug_report_url}}}) issue from one of the Mergington High School students trying to access the website. + +Let's use GitHub Copilot Agent Mode together with GitHub MCP server to work on that issue. + +1. Open the **Copilot Chat Agent Mode** panel in VS Code and ensure that GitHub MCP Server is active, and the tools are enabled + + + +1. Ask Copilot if there are any open bug reports on your repository: + + > **Prompt** + > + > ```prompt + > Are there any open bug report issues on my repository ({{{full_repo_name}}}) ? + > ``` + + > 🪧 **Note:** We explicitly include the repository name to add it to Copilot's session context. For subsequent prompts, this context will be preserved in the conversation. + + > ✨ **Bonus:** You are welcome to try the prompt without it and if Copilot chooses to list issues in a different repository, guide it your way. + +1. Once Copilot identifies a bug report, ask it to implement a fix: + + > **Prompt** + > + > ```prompt + > Okay, let's start working on the bug report. + > Comment on the issue that I will take a look at the issue and try to fix it. + > Create a new branch, introduce the changes and raise a pull request to the `main` branch + > ``` + + > ⚠️ **Warning:** Always verify the information that Copilot passes to the MCP server before accepting. + +1. Follow Copilot's guidance to create a pull request with the fix. + + > 🪧 **Note:** Remember that Copilot is conversational, and you are power of guiding the AI assistant to implement what's on your mind. + +1. Once the pull request is created, Mona will start checking your work. Give her a moment and keep watch of the comments. You will see her respond with progress info and the next step! + +
+Having trouble?
+ +If you encounter issues: + +- Make sure your MCP configuration is properly set up from Step 1 +- Verify that the tools are executed on your repository ({{{full_repo_name}}}). Whenever a MCP tool is invoked you can inspect what Copilot passes to the MCP Server. + +
diff --git a/.github/steps/3-step.md b/.github/steps/3-step.md new file mode 100644 index 0000000..7ccfef1 --- /dev/null +++ b/.github/steps/3-step.md @@ -0,0 +1,29 @@ +## Step 3: Validating AI-generated code + +Great work on implementing the fix with Copilot! + +While AI assistants like GitHub Copilot can dramatically improve productivity, it's essential to remember that you are responsible for reviewing and validating all generated code. + +> [!tip] +> Whenever working on real projects, use [GitHub Actions](https://github.com/features/actions) to setup automated testing for your projects. + +### :keyboard: Activity: Review and merge the AI solution + +1. Examine the [pull request]({{{pull_request_url}}}) Copilot helped you create. + +> ✨ **Bonus:** If your access is sufficient, you can ask Copilot to review your code. + +1. Once you're satisfied with the changes, merge the pull request + + > 🪧 **Note:** If you are not satisfied, go back to the codespace and work with Copilot to introduce the changes. Remember, coding is an iterative process. + +
+Having trouble?
+ +If you encounter issues: + +- Make sure the PR was created correctly in the previous step +- If needed, you can navigate directly to your repository on GitHub.com to find the PR +- Remember that while MCP helps Copilot understand your repo better, human review is still essential + +
diff --git a/.github/steps/4-step.md b/.github/steps/4-step.md new file mode 100644 index 0000000..2bd10da --- /dev/null +++ b/.github/steps/4-step.md @@ -0,0 +1,24 @@ +## Step 4: Wrap up on the bug report + + +### :keyboard: Activity: Wrap up the bug report + +1. Open the **Copilot Chat Agent Mode** session in VS Code. +1. Ask Copilot to comment on the bug report issue we just worked on. And include a little bonus for the proactive student! + + > **Prompt** + > + > ```prompt + > Add a closing comment on the bug report issue that the issue has been resolved. + > Include a sneak peek of what to expect in the extracurricular GitHub Skills activity (top 5 most starred repositories in GitHub `skills` organization) + > ``` + +
+ Having trouble? 🤷
+ + Some things to check + - Is your MCP Server still running? + - Check what information is passed to the MCP server calls - is Copilot using the correct repository? + - Did Copilot comment on the bug report? +
+ diff --git a/.github/steps/x-review.md b/.github/steps/x-review.md new file mode 100644 index 0000000..f007c48 --- /dev/null +++ b/.github/steps/x-review.md @@ -0,0 +1,18 @@ +## Review + +_Congratulations, you've completed this Exercise and joined the world of developers!_ + +celebrate + +Here's a recap of your accomplishments: + + +### What's next? + + +Check out these resources to learn more or get involved: + +- [Take another GitHub Skills exercise](https://skills.github.com). +_ + +__ diff --git a/.github/workflows/0-start-exercise.yml b/.github/workflows/0-start-exercise.yml new file mode 100644 index 0000000..68442c6 --- /dev/null +++ b/.github/workflows/0-start-exercise.yml @@ -0,0 +1,74 @@ +name: Step 0 # Start Exercise + +on: + push: + branches: + - main + +permissions: + contents: write # Update Readme + actions: write # Disable/enable workflows + issues: write # Create issue and comment on issues + + +env: + STEP_1_FILE: ".github/steps/1-step.md" + +jobs: + start_exercise: + if: | + !github.event.repository.is_template + name: Start Exercise + uses: skills/exercise-toolkit/.github/workflows/start-exercise.yml@v0.1.0 + with: + exercise-title: "Integrate MCP with Copilot" + intro-message: "Welcome to the exercise! This exercise will help you learn how to integrate the Model Context Protocol (MCP) with GitHub Copilot." + + + post_next_step_content: + name: Post next step content + runs-on: ubuntu-latest + needs: [start_exercise] + env: + ISSUE_URL: ${{ needs.start_exercise.outputs.issue-url }} + + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get response templates + uses: actions/checkout@v4 + with: + repository: skills/exercise-toolkit + path: exercise-toolkit + ref: v0.1.0 + + - name: Build comment - add step content + id: build-comment + uses: skills/action-text-variables@v1 + with: + template-file: ${{ env.STEP_1_FILE }} + template-vars: | + full_repo_name=${{ github.repository }} + + - name: Create comment - add step content + run: | + gh issue comment "$ISSUE_URL" \ + --body "$ISSUE_BODY" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ISSUE_BODY: ${{ steps.build-comment.outputs.updated-text }} + + - name: Create comment - watching for progress + run: | + gh issue comment "$ISSUE_URL" \ + --body-file exercise-toolkit/markdown-templates/step-feedback/watching-for-progress.md + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Disable current workflow and enable next one + run: | + gh workflow enable "Step 1" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/1-step.yml b/.github/workflows/1-step.yml new file mode 100644 index 0000000..6974118 --- /dev/null +++ b/.github/workflows/1-step.yml @@ -0,0 +1,166 @@ +name: Step 1 + +on: + push: + paths: + - ".vscode/mcp.json" + +permissions: + contents: read + actions: write + issues: write + +env: + STEP_2_FILE: ".github/steps/2-step.md" + +jobs: + find_exercise: + name: Find Exercise Issue + uses: skills/exercise-toolkit/.github/workflows/find-exercise-issue.yml@v0.1.0 + + check_step_work: + name: Check step work + runs-on: ubuntu-latest + needs: find_exercise + env: + ISSUE_URL: ${{ needs.find_exercise.outputs.issue-url }} + + steps: + - name: Get response templates + uses: actions/checkout@v4 + with: + repository: skills/exercise-toolkit + path: exercise-toolkit + ref: v0.1.0 + + - name: Update comment - checking work + run: | + gh issue comment "$ISSUE_URL" \ + --body-file exercise-toolkit/markdown-templates/step-feedback/checking-work.md \ + --edit-last + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # TODO: Check for keywords (github, server)? + - name: Build message - step finished + id: build-message-step-finish + uses: skills/action-text-variables@v1 + with: + template-file: exercise-toolkit/markdown-templates/step-feedback/step-finished-prepare-next-step.md + template-vars: | + next_step_number=2 + + - name: Update comment - step finished + run: | + gh issue comment "$ISSUE_URL" \ + --body "$ISSUE_BODY" \ + --edit-last + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ISSUE_BODY: ${{ steps.build-message-step-finish.outputs.updated-text }} + + create_bug_report: + name: Create bug report + needs: [check_step_work] + runs-on: ubuntu-latest + + outputs: + bug-report-url: ${{ steps.create-bug-issue.outputs.bug_report_url }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Create bug issue + id: create-bug-issue + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const bugReport = ` + # 🚨 Missing GitHub Skills Activity + + ## 🐛 Description of the Issue + The GitHub Skills activity announced by our principal is missing from the school activities signup page. + + Yesterday in the school assembly, the principal announced a new partnership with GitHub to teach students practical coding and collaboration skills. However, when I try to sign up for this activity, it's nowhere to be found on the website. + + I can see the following activities: + - ✅ Chess Club + - ✅ Programming Class + - ✅ Gym Class + + But there is: + - ❌ NO GitHub Skills activity + + ## ⏱️ Timeline + This is time-sensitive as the announcement mentioned registrations would close by the end of this week. Many students are eager to join as it may help with college applications. + + ## 💡 Expected Outcome + The GitHub Skills activity should be added to the system and available for registration like other activities + + Mona + 11th Grade Student + mona@mergington.edu + `; + + const response = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: '🔍 Missing GitHub Skills Activity on School Website', + body: bugReport, + labels: ['bug'] + }); + + console.log(`Bug report created: ${response.data.html_url}`); + core.setOutput('bug_report_url', response.data.html_url); + + post_next_step_content: + name: Post next step content + needs: [find_exercise, check_step_work, create_bug_report] + runs-on: ubuntu-latest + env: + ISSUE_URL: ${{ needs.find_exercise.outputs.issue-url }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get response templates + uses: actions/checkout@v4 + with: + repository: skills/exercise-toolkit + path: exercise-toolkit + ref: v0.1.0 + + - name: Build comment - add step content + id: build-comment + uses: skills/action-text-variables@v1 + with: + template-file: ${{ env.STEP_2_FILE }} + template-vars: | + full_repo_name=${{ github.repository }} + bug_report_url=${{ needs.create_bug_report.outputs.bug-report-url }} + + - name: Create comment - add step content + run: | + gh issue comment "$ISSUE_URL" \ + --body "$ISSUE_BODY" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ISSUE_BODY: ${{ steps.build-comment.outputs.updated-text }} + + + - name: Create comment - watching for progress + run: | + gh issue comment "$ISSUE_URL" \ + --body-file exercise-toolkit/markdown-templates/step-feedback/watching-for-progress.md + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Disable current workflow and enable next one + run: | + gh workflow disable "Step 1" + gh workflow enable "Step 2" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/2-step.yml b/.github/workflows/2-step.yml new file mode 100644 index 0000000..44c45be --- /dev/null +++ b/.github/workflows/2-step.yml @@ -0,0 +1,117 @@ +name: Step 2 + + +on: + pull_request: + branches: + - main + paths: + - "src/app.py" + +permissions: + contents: read + actions: write + issues: write + +env: + STEP_3_FILE: ".github/steps/3-step.md" + + +jobs: + find_exercise: + name: Find Exercise Issue + uses: skills/exercise-toolkit/.github/workflows/find-exercise-issue.yml@v0.1.0 + + check_step_work: + name: Check step work + runs-on: ubuntu-latest + needs: find_exercise + if: | + !github.event.repository.is_template + env: + ISSUE_URL: ${{ needs.find_exercise.outputs.issue-url }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get response templates + uses: actions/checkout@v4 + with: + repository: skills/exercise-toolkit + path: exercise-toolkit + ref: v0.1.0 + + + - name: Update comment - checking work + run: | + gh issue comment "$ISSUE_URL" \ + --body-file exercise-toolkit/markdown-templates/step-feedback/checking-work.md \ + --edit-last + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Build message - step finished + id: build-message-step-finish + uses: skills/action-text-variables@v1 + with: + template-file: exercise-toolkit/markdown-templates/step-feedback/step-finished-prepare-next-step.md + template-vars: | + next_step_number=3 + + - name: Update comment - step finished + run: | + gh issue comment "$ISSUE_URL" \ + --body "${{ steps.build-message-step-finish.outputs.updated-text }}" \ + --edit-last + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + post_next_step_content: + name: Post next step content + needs: [find_exercise, check_step_work] + runs-on: ubuntu-latest + env: + ISSUE_URL: ${{ needs.find_exercise.outputs.issue-url }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get response templates + uses: actions/checkout@v4 + with: + repository: skills/exercise-toolkit + path: exercise-toolkit + ref: v0.1.0 + + - name: Build comment - add step content + id: build-comment + uses: skills/action-text-variables@v1 + with: + template-file: ${{ env.STEP_3_FILE }} + template-vars: | + pull_request_url=${{ github.event.pull_request.html_url }} + + - name: Create comment - add step content + run: | + gh issue comment "$ISSUE_URL" \ + --body "$ISSUE_BODY" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ISSUE_BODY: ${{ steps.build-comment.outputs.updated-text }} + + + - name: Create comment - watching for progress + run: | + gh issue comment "$ISSUE_URL" \ + --body-file exercise-toolkit/markdown-templates/step-feedback/watching-for-progress.md + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Disable current workflow and enable next one + run: | + gh workflow disable "Step 2" + gh workflow enable "Step 3" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/3-step.yml b/.github/workflows/3-step.yml new file mode 100644 index 0000000..8c96caa --- /dev/null +++ b/.github/workflows/3-step.yml @@ -0,0 +1,106 @@ +name: Step 3 +on: + pull_request: + branches: + - main + types: + - closed + + +permissions: + contents: read + actions: write + issues: write + +env: + STEP_4_FILE: ".github/steps/4-step.md" + +jobs: + find_exercise: + name: Find Exercise Issue + uses: skills/exercise-toolkit/.github/workflows/find-exercise-issue.yml@v0.1.0 + + check_step_work: + name: Check step work + runs-on: ubuntu-latest + needs: find_exercise + if: | + !github.event.repository.is_template && + github.event.pull_request.merged == true + env: + ISSUE_URL: ${{ needs.find_exercise.outputs.issue-url }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get response templates + uses: actions/checkout@v4 + with: + repository: skills/exercise-toolkit + path: exercise-toolkit + ref: v0.1.0 + + - name: Update comment - checking work + run: | + gh issue comment "$ISSUE_URL" \ + --body-file exercise-toolkit/markdown-templates/step-feedback/checking-work.md \ + --edit-last + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Build message - step finished + id: build-message-step-finish + uses: skills/action-text-variables@v1 + with: + template-file: exercise-toolkit/markdown-templates/step-feedback/step-finished-prepare-next-step.md + template-vars: | + next_step_number=4 + + - name: Update comment - step finished + run: | + gh issue comment "$ISSUE_URL" \ + --body "${{ steps.build-message-step-finish.outputs.updated-text }}" \ + --edit-last + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + post_next_step_content: + name: Post next step content + needs: [find_exercise, check_step_work] + runs-on: ubuntu-latest + env: + ISSUE_URL: ${{ needs.find_exercise.outputs.issue-url }} + + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get response templates + uses: actions/checkout@v4 + with: + repository: skills/exercise-toolkit + path: exercise-toolkit + ref: v0.1.0 + + - name: Create comment - add step content + run: | + gh issue comment "$ISSUE_URL" \ + --body-file ${{ env.STEP_4_FILE }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create comment - watching for progress + run: | + gh issue comment "$ISSUE_URL" \ + --body-file exercise-toolkit/markdown-templates/step-feedback/watching-for-progress.md + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Disable current workflow and enable next one + run: | + gh workflow disable "Step 3" + gh workflow enable "Step 4" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/4-step.yml b/.github/workflows/4-step.yml new file mode 100644 index 0000000..db4a719 --- /dev/null +++ b/.github/workflows/4-step.yml @@ -0,0 +1,88 @@ +name: Step 4 + +on: + issue_comment: + types: [created] + +permissions: + contents: write + actions: write + issues: write + +# concurrency: +# group: ${{ github.workflow }} +# cancel-in-progress: true + +env: + REVIEW_FILE: ".github/steps/x-review.md" + +jobs: + find_exercise: + name: Find Exercise Issue + uses: skills/exercise-toolkit/.github/workflows/find-exercise-issue.yml@v0.1.0 + + check_step_work: + name: Check step work + needs: find_exercise + runs-on: ubuntu-latest + if: | + !github.event.repository.is_template + env: + ISSUE_URL: ${{ needs.find_exercise.outputs.issue-url }} + + steps: + - name: Get response templates + uses: actions/checkout@v4 + with: + repository: skills/exercise-toolkit + path: exercise-toolkit + ref: v0.1.0 + + - name: Update comment - step finished - final review next + run: | + gh issue comment "$ISSUE_URL" \ + --body-file exercise-toolkit/markdown-templates/step-feedback/lesson-review.md \ + --edit-last + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + post_review_content: + name: Post review content + needs: [find_exercise, check_step_work] + runs-on: ubuntu-latest + if: | + !github.event.repository.is_template + env: + ISSUE_URL: ${{ needs.find_exercise.outputs.issue-url }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Create comment - add review content + run: | + gh issue comment "$ISSUE_URL" \ + --body-file ${{ env.REVIEW_FILE }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + finish_exercise: + name: Finish Exercise + needs: [find_exercise, post_review_content] + uses: skills/exercise-toolkit/.github/workflows/finish-exercise.yml@v0.1.0 + with: + issue-url: ${{ needs.find_exercise.outputs.issue-url }} + + disable_workflow: + name: Disable this workflow + needs: [find_exercise, post_review_content] + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Disable current workflow + run: gh workflow disable "${{github.workflow}}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0eace22 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + + +# Environment variables +.env diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b349f44 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Mergington WebApp", + "type": "debugpy", + "request": "launch", + "module": "uvicorn", + "args": [ + "src.app:app", + "--reload" + ], + "jinja": true + } + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..07e5acb --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# Integrate MCP with GitHub Copilot + + + +![](../../actions/workflows/1-create-a-branch.yml/badge.svg) +![](../../actions/workflows/2-commit-a-file.yml/badge.svg) +![](../../actions/workflows/3-open-a-pull-request.yml/badge.svg) +![](../../actions/workflows/4-merge-your-pull-request.yml/badge.svg) + +_Learn how to integrate Model Context Protocol (MCP) with GitHub Copilot in less than an hour._ + +## Welcome + +The Model Context Protocol (MCP) provides powerful ways to enhance AI interactions in your development workflow. By integrating MCP with GitHub Copilot, you can leverage context-aware AI assistance to build more sophisticated applications. This exercise guides you through everything you need to start using MCP with GitHub Copilot in less than an hour. + +- **Who is this for**: Developers looking to enhance their AI-assisted workflows, GitHub Copilot users, and AI enthusiasts. +- **What you'll learn**: We'll introduce MCP basics, GitHub MCP server setup, and integration with Copilot Agent Mode. + +- **Prerequisites**: [Getting Started with Copilot Exercise](https://github.com/skills/getting-started-with-github-copilot) +- **How long**: This exercise takes less than one hour to complete. + +In this exercise, you will: + +1. Learn the basics of Model Context Protocol (MCP) +2. Set up a GitHub MCP server +3. Integrate MCP with GitHub Copilot Agent Mode +4. Create a sample application showcasing the integration + +### How to start this exercise + +1. Right-click **Copy Exercise** and open the link in a new tab. + + + + + +2. In the new tab, most of the prompts will automatically fill in for you. + + - For owner, choose your personal account or an organization to host the repository. + - We recommend creating a public repository, as private repositories will [use Actions minutes](https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions). + - Scroll down and click the **Create repository** button at the bottom of the form. + +3. After your new repository is created, wait about 20 seconds for the exercise to be prepared and buttons updated. You will continue working from your copy of the exercise. + + - The **Copy Exercise** button will deactivate, changing to gray. + - The **Start Exercise** button will activate, changing to green. + - You will likely need to refresh the page. + +4. Click **Start Exercise**. Follow the step-by-step instructions and feedback will be provided as you progress. + + + + + +> [!IMPORTANT] +> The **Start Exercise** button will activate after copying the repository. You will probably need to refresh the page. + +--- + +Get help: [Post in our discussion board](https://github.com/orgs/skills/discussions/categories/introduction-to-github) • [Review the GitHub status page](https://www.githubstatus.com/) + +© 2025 GitHub • [Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/code_of_conduct.md) • [MIT License](https://gh.io/mit) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..97dc7cd --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +fastapi +uvicorn diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..a90534b --- /dev/null +++ b/src/README.md @@ -0,0 +1,50 @@ +# Mergington High School Activities API + +A super simple FastAPI application that allows students to view and sign up for extracurricular activities. + +## Features + +- View all available extracurricular activities +- Sign up for activities + +## Getting Started + +1. Install the dependencies: + + ``` + pip install fastapi uvicorn + ``` + +2. Run the application: + + ``` + python app.py + ``` + +3. Open your browser and go to: + - API documentation: http://localhost:8000/docs + - Alternative documentation: http://localhost:8000/redoc + +## API Endpoints + +| Method | Endpoint | Description | +| ------ | ----------------------------------------------------------------- | ------------------------------------------------------------------- | +| GET | `/activities` | Get all activities with their details and current participant count | +| POST | `/activities/{activity_name}/signup?email=student@mergington.edu` | Sign up for an activity | + +## Data Model + +The application uses a simple data model with meaningful identifiers: + +1. **Activities** - Uses activity name as identifier: + + - Description + - Schedule + - Maximum number of participants allowed + - List of student emails who are signed up + +2. **Students** - Uses email as identifier: + - Name + - Grade level + +All data is stored in memory, which means data will be reset when the server restarts. diff --git a/src/app.py b/src/app.py new file mode 100644 index 0000000..4ebb1d9 --- /dev/null +++ b/src/app.py @@ -0,0 +1,67 @@ +""" +High School Management System API + +A super simple FastAPI application that allows students to view and sign up +for extracurricular activities at Mergington High School. +""" + +from fastapi import FastAPI, HTTPException +from fastapi.staticfiles import StaticFiles +from fastapi.responses import RedirectResponse +import os +from pathlib import Path + +app = FastAPI(title="Mergington High School API", + description="API for viewing and signing up for extracurricular activities") + +# Mount the static files directory +current_dir = Path(__file__).parent +app.mount("/static", StaticFiles(directory=os.path.join(Path(__file__).parent, + "static")), name="static") + +# In-memory activity database +activities = { + "Chess Club": { + "description": "Learn strategies and compete in chess tournaments", + "schedule": "Fridays, 3:30 PM - 5:00 PM", + "max_participants": 12, + "participants": ["michael@mergington.edu", "daniel@mergington.edu"] + }, + "Programming Class": { + "description": "Learn programming fundamentals and build software projects", + "schedule": "Tuesdays and Thursdays, 3:30 PM - 4:30 PM", + "max_participants": 20, + "participants": ["emma@mergington.edu", "sophia@mergington.edu"] + }, + "Gym Class": { + "description": "Physical education and sports activities", + "schedule": "Mondays, Wednesdays, Fridays, 2:00 PM - 3:00 PM", + "max_participants": 30, + "participants": ["john@mergington.edu", "olivia@mergington.edu"] + } +} + + +@app.get("/") +def root(): + return RedirectResponse(url="/static/index.html") + + +@app.get("/activities") +def get_activities(): + return activities + + +@app.post("/activities/{activity_name}/signup") +def signup_for_activity(activity_name: str, email: str): + """Sign up a student for an activity""" + # Validate activity exists + if activity_name not in activities: + raise HTTPException(status_code=404, detail="Activity not found") + + # Get the specific activity + activity = activities[activity_name] + + # Add student + activity["participants"].append(email) + return {"message": f"Signed up {email} for {activity_name}"} diff --git a/src/static/app.js b/src/static/app.js new file mode 100644 index 0000000..dcc1e38 --- /dev/null +++ b/src/static/app.js @@ -0,0 +1,86 @@ +document.addEventListener("DOMContentLoaded", () => { + const activitiesList = document.getElementById("activities-list"); + const activitySelect = document.getElementById("activity"); + const signupForm = document.getElementById("signup-form"); + const messageDiv = document.getElementById("message"); + + // Function to fetch activities from API + async function fetchActivities() { + try { + const response = await fetch("/activities"); + const activities = await response.json(); + + // Clear loading message + activitiesList.innerHTML = ""; + + // Populate activities list + Object.entries(activities).forEach(([name, details]) => { + const activityCard = document.createElement("div"); + activityCard.className = "activity-card"; + + const spotsLeft = details.max_participants - details.participants.length; + + activityCard.innerHTML = ` +

${name}

+

${details.description}

+

Schedule: ${details.schedule}

+

Availability: ${spotsLeft} spots left

+ `; + + activitiesList.appendChild(activityCard); + + // Add option to select dropdown + const option = document.createElement("option"); + option.value = name; + option.textContent = name; + activitySelect.appendChild(option); + }); + } catch (error) { + activitiesList.innerHTML = "

Failed to load activities. Please try again later.

"; + console.error("Error fetching activities:", error); + } + } + + // Handle form submission + signupForm.addEventListener("submit", async (event) => { + event.preventDefault(); + + const email = document.getElementById("email").value; + const activity = document.getElementById("activity").value; + + try { + const response = await fetch( + `/activities/${encodeURIComponent(activity)}/signup?email=${encodeURIComponent(email)}`, + { + method: "POST", + } + ); + + const result = await response.json(); + + if (response.ok) { + messageDiv.textContent = result.message; + messageDiv.className = "success"; + signupForm.reset(); + } else { + messageDiv.textContent = result.detail || "An error occurred"; + messageDiv.className = "error"; + } + + messageDiv.classList.remove("hidden"); + + // Hide message after 5 seconds + setTimeout(() => { + messageDiv.classList.add("hidden"); + }, 5000); + } catch (error) { + messageDiv.textContent = "Failed to sign up. Please try again."; + messageDiv.className = "error"; + messageDiv.classList.remove("hidden"); + console.error("Error signing up:", error); + } + }); + + // Initialize app + fetchActivities(); +}); diff --git a/src/static/index.html b/src/static/index.html new file mode 100644 index 0000000..3074f6e --- /dev/null +++ b/src/static/index.html @@ -0,0 +1,50 @@ + + + + + + Mergington High School Activities + + + +
+

Mergington High School

+

Extracurricular Activities

+
+ +
+
+

Available Activities

+
+ +

Loading activities...

+
+
+ +
+

Sign Up for an Activity

+
+
+ + +
+
+ + +
+ +
+ +
+
+ + + + + + diff --git a/src/static/styles.css b/src/static/styles.css new file mode 100644 index 0000000..a533b32 --- /dev/null +++ b/src/static/styles.css @@ -0,0 +1,144 @@ +* { + box-sizing: border-box; + margin: 0; + padding: 0; + font-family: Arial, sans-serif; +} + +body { + font-family: Arial, sans-serif; + line-height: 1.6; + color: #333; + max-width: 1200px; + margin: 0 auto; + padding: 20px; + background-color: #f5f5f5; +} + +header { + text-align: center; + padding: 20px 0; + margin-bottom: 30px; + background-color: #1a237e; + color: white; + border-radius: 5px; +} + +header h1 { + margin-bottom: 10px; +} + +main { + display: flex; + flex-wrap: wrap; + gap: 30px; + justify-content: center; +} + +@media (min-width: 768px) { + main { + grid-template-columns: 2fr 1fr; + } +} + +section { + background-color: white; + padding: 20px; + border-radius: 5px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 500px; +} + +section h3 { + margin-bottom: 20px; + padding-bottom: 10px; + border-bottom: 1px solid #ddd; + color: #1a237e; +} + +.activity-card { + margin-bottom: 15px; + padding: 15px; + border: 1px solid #ddd; + border-radius: 5px; + background-color: #f9f9f9; +} + +.activity-card h4 { + margin-bottom: 10px; + color: #0066cc; +} + +.activity-card p { + margin-bottom: 8px; +} + +.form-group { + margin-bottom: 15px; +} + +.form-group label { + display: block; + margin-bottom: 5px; + font-weight: bold; +} + +.form-group input, +.form-group select { + width: 100%; + padding: 10px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 16px; +} + +button { + background-color: #1a237e; + color: white; + border: none; + padding: 10px 15px; + font-size: 16px; + border-radius: 5px; + cursor: pointer; + transition: background-color 0.2s; +} + +button:hover { + background-color: #3949ab; +} + +.message { + margin-top: 20px; + padding: 10px; + border-radius: 4px; +} + +.success { + background-color: #e8f5e9; + color: #2e7d32; + border: 1px solid #a5d6a7; +} + +.error { + background-color: #ffebee; + color: #c62828; + border: 1px solid #ef9a9a; +} + +.info { + background-color: #d1ecf1; + color: #0c5460; + border: 1px solid #bee5eb; +} + +.hidden { + display: none; +} + +footer { + text-align: center; + margin-top: 30px; + padding: 20px; + color: #666; +}