A modular Model Context Protocol (MCP) server that provides AI assistants with access to course schedules, exams, announcements, lecture notes, and integrations with Notion and Google Calendar.
- Course Schedule Management: Query upcoming exams, lectures, and assignments
- Lecture Summaries: Access and search through lecture notes with fuzzy matching
- Announcements: Check latest course announcements
- Notion Integration: Write notes and content to Notion pages
- Google Calendar: Add meetings and events via n8n webhook
- Modular Architecture: Clean, maintainable codebase with separation of concerns
- Node.js (v18 or higher)
- npm or yarn
- A Notion integration token (optional, for Notion features)
- An n8n webhook URL (optional, for Google Calendar features)
- Clone the repository:
git clone <repository-url>
cd ClassIO- Install dependencies:
npm install- Create a
.envfile based on.env.example:
cp .env.example .env- Configure your environment variables in
.env:
NOTION_TOKEN=your_notion_token_here
DEFAULT_BLOCK_ID=your_default_block_id_here
N8N_WEBHOOK_URL=your_webhook_url_here
PORT=8000- Go to https://www.notion.so/my-integrations
- Click "New integration"
- Give it a name and select your workspace
- Copy the "Internal Integration Token"
- Share your Notion pages with the integration
- Create a workflow in n8n
- Add a "Webhook" trigger node
- Copy the webhook URL
- Add it to your
.envfile
npm run watchnpm run build
npm startThe server will start on http://localhost:8000/mcp (or the port specified in your .env).
Add this configuration to your MCP client settings:
{
"mcpServers": {
"classio": {
"url": "http://localhost:8000/mcp"
}
}
}To expose your MCP server to cloud-based AI tools:
- Install ngrok:
# macOS
brew install ngrok
# Linux
snap install ngrok
# Windows
choco install ngrok- Start your MCP server:
npm start- Expose the server with ngrok:
ngrok http 8000-
Copy the public URL from ngrok output (e.g.,
https://abc123.ngrok.io) -
Configure your AI tool with the ngrok URL:
{
"mcpServers": {
"classio": {
"url": "https://abc123.ngrok.io/mcp"
}
}
}Any AI tool with MCP support can connect to this server:
- Claude Desktop (via local or ngrok)
- Claude.ai (via ngrok)
- Custom AI applications using the MCP SDK
- Other MCP-compatible clients
Get the next upcoming exam for a specific course.
Example: "When is my next exam in CSC254?"
Get next exams across all courses.
Example: "Show me all my upcoming exams"
Find the 3 most recent lectures before a given date.
Example: "What topics did we cover recently in CSC160?"
Get detailed content from a specific lecture using fuzzy name matching.
Example: "Give me a summary of the data structures lecture"
Create and write content to a Notion page.
Example: "Create a study guide page in my CSC254 notebook"
Add events to Google Calendar via n8n.
Example: "Add a meeting to Google Calendar for tomorrow at 2pm"
Check recent announcements for a class.
Example: "Any announcements about the curve in CSC160?"
ClassIO/
├── src/
│ ├── index.ts # Entry point
│ ├── server.ts # Express & MCP server setup
│ ├── config/
│ │ └── environment.ts # Environment variables & validation
│ ├── types/
│ │ ├── schedule.ts # Schedule/exam type definitions
│ │ ├── announcement.ts # Announcement type definitions
│ │ ├── lecture.ts # Lecture type definitions
│ │ └── index.ts # Type exports
│ ├── utils/
│ │ ├── constants.ts # Paths & constants
│ │ ├── string-similarity.ts # Fuzzy matching utilities
│ │ ├── file-operations.ts # File reading utilities
│ │ └── index.ts # Utility exports
│ ├── services/
│ │ ├── notion-service.ts # Notion API integration
│ │ ├── webhook-service.ts # N8N webhook integration
│ │ └── index.ts # Service exports
│ └── tools/
│ ├── get-next-course-exam.ts
│ ├── get-all-course-next-exam.ts
│ ├── find-closest-lectures.ts
│ ├── summarize-lectures.ts
│ ├── notion-write.ts
│ ├── google-calendar.ts
│ ├── latest-announcement-check.ts
│ └── index.ts # Tool registry
├── static/
│ ├── csc160/
│ │ ├── schedule.json
│ │ ├── announcements/
│ │ └── lectures/
│ ├── csc171/
│ └── csc254/
├── build/ # Compiled JavaScript
├── .env # Environment variables (not tracked)
├── .env.example # Environment template
└── package.json
The codebase follows a modular architecture with clear separation of concerns:
config/: Environment configuration and validationtypes/: TypeScript type definitions for type safetyutils/: Shared utility functions (string matching, file operations, constants)services/: External API integrations (Notion, webhooks)tools/: Individual MCP tool implementationsserver.ts: Express and MCP server configurationindex.ts: Application entry point
This structure makes the codebase:
- Maintainable: Each module has a single responsibility
- Testable: Components can be tested in isolation
- Scalable: Easy to add new tools or services
- Type-safe: Centralized type definitions prevent errors
- Create a folder in
static/with your course ID (e.g.,csc999) - Add a
schedule.jsonfile:
{
"course_code": "CSC999",
"schedule": [
{
"date": "2025-01-20",
"type": "lecture",
"topic": "Introduction",
"className": "Lecture 1"
},
{
"date": "2025-02-15",
"type": "exam",
"topic": "Midterm Exam",
"className": "Midterm"
}
]
}- (Optional) Add lecture notes in
lectures/as.txtfiles - (Optional) Add announcements in
announcements/announcement.json
Thanks to the modular architecture, adding a new tool is straightforward:
- Create a new file in
src/tools/(e.g.,my-new-tool.ts) - Implement the tool:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
export function registerMyNewTool(server: McpServer): void {
server.registerTool(
"my_new_tool",
{
title: "My New Tool",
description: "Description of what the tool does",
inputSchema: z.object({
param: z.string().describe("Parameter description"),
}),
},
async (args) => {
// Tool implementation
return {
content: [{ type: "text", text: "Result" }],
};
},
);
}- Register the tool in
src/tools/index.ts:
import { registerMyNewTool } from "./my-new-tool.js";
export function registerAllTools(server: McpServer): void {
// ... existing tools
registerMyNewTool(server);
}npm run buildnpm run watchnpm run inspector- Never commit
.env- it contains sensitive credentials - Rotate credentials after making the repository public
- Use ngrok auth for production deployments
- Limit ngrok session time for temporary access
- Check that port 8000 is not already in use
- Verify all environment variables are set in
.env - Run
npm run buildto ensure compilation is successful
- Ensure you've shared your Notion pages with the integration
- Verify the
NOTION_TOKENis correct - Check that
DEFAULT_BLOCK_IDpoints to a valid page
- Ensure your local server is running before starting ngrok
- Check firewall settings
- Verify the ngrok URL matches in your AI tool configuration
MIT
Contributions are welcome! Please open an issue or submit a pull request.