AI Student Developer Assistant
README.md
# AI Student Developer Assistant — MCP Server
A production-quality [Model Context Protocol](https://modelcontextprotocol.io) server that gives an AI assistant unified access to your **GitHub issues**, your **academic deadlines** (LMS), and a **personal task tracker** — so it can answer questions like *"What should I work on today?"* with a real, prioritized answer.
> Built with Python 3.12+, the official MCP Python SDK (v2), FastAPI-style service separation, SQLite, and httpx. Fully tested with mocked external APIs — no real credentials required to run the suite.
---
## Table of Contents
- [Project Overview](#project-overview)
- [Features — the MCP tools](#features--the-mcp-tools)
- [Architecture](#architecture)
- [Tech Stack](#tech-stack)
- [Installation](#installation)
- [Configuration](#configuration)
- [Running the Server](#running-the-server)
- [Connecting an AI Client](#connecting-an-ai-client)
- [Example Usage](#example-usage)
- [API Integration](#api-integration)
- [Database](#database)
- [Testing](#testing)
- [Security](#security)
- [Limitations](#limitations)
- [Future Improvements](#future-improvements)
- [Project Architecture](#project-architecture)
- [Demo Scenario](#demo-scenario)
- [Interview Talking Points](#interview-talking-points)
---
## Project Overview
**The problem.** A student-developer's work lives in three disconnected places: code tasks in GitHub, assignments and exams in a college LMS, and personal todos scattered in notes. Priorities get decided by memory, so things slip.
**The solution.** One MCP server that exposes all three sources as small, well-described, strongly-typed **tools**. An AI assistant reads and reasons over all of them at once: it can pull your assigned issues, this week's deadlines, your pending tasks, detect overdue items, build a prioritized summary — and *mutate* systems (create/close issues, create tasks, batch-import deadlines) with the same interface.
**Status.** This is a portfolio-quality implementation of a *personal productivity tool*. Everything works end-to-end; the LMS integration is deliberately mocked behind a swappable interface (see [Limitations](#limitations)).
---
## Features — the MCP tools
Fifteen narrowly-scoped tools. Each has a clear name, a description the AI reads to decide when to call it, validated inputs, and a predictable output:
### GitHub (4 tools)
| Tool | Description |
|---|---|
| `get_open_issues` | List open issues; filter by repository (`owner/name`), assignee, labels, state. Without a repository, returns issues **assigned to you** across all repos. |
| `get_issue` | Full detail (body, labels, assignee) for one issue. |
| `create_issue` | Create a GitHub issue. |
| `close_issue` | Close a GitHub issue. |
### LMS / academic deadlines (3 tools)
| Tool | Description |
|---|---|
| `get_upcoming_deadlines` | Assignments/exams, optionally filtered by date range and course. |
| `get_course_assignments` | All assignments for one course. |
| `get_assignment` | Detailed description of one assignment. |
### Task tracker (8 tools)
| Tool | Description |
|---|---|
| `create_task` | Add a personal task with title, description, due date, priority. |
| `get_tasks` | List/filter tasks by status, priority, due-date window, source. |
| `complete_task` | Mark a task done. |
| `delete_task` | Remove a task. |
| `get_overdue_tasks` | Tasks past their due date and not completed. |
| `create_task_from_issue` | GitHub issue → task (duplicate-safe). |
| `create_tasks_from_deadlines` | Deadlines → tasks (duplicate-safe). |
| `get_workload_summary` | One unified snapshot: open issues + deadlines + pending/overdue tasks. |
All tools return the same JSON shape so an agent can parse results reliably:
```json
{ "ok": true, "data": { "...": "..." }, "error": null }
{ "ok": false, "data": null, "error": { "code": "not_found", "message": "..." } }
```
---
## Architecture
```mermaid
flowchart TB
subgraph Host["AI Client (e.g. Claude Desktop)"]
Agent["Assistant / Agent"]
end
subgraph MCP["MCP Protocol (stdio)"]
S["MCPServer (mcp SDK v2)"]
end
subgraph App["app/"]
Tools["tools/ · 15 thin tool functions"]
Services["services/ · GitHub · LMS · Task"]
Repo["TaskRepository"]
DB[("SQLite")]
Mock["MockLMSService"]
end
Ext["GitHub REST API v3"]
Agent -->|tools/list · tools/call · server/discover| S
S --> Tools
Tools --> Services --> Repo --> DB
Services --> Ext
Services --> Mock
```
**The golden rule in this codebase:** the MCP layer is *only* adapters. Each tool function validates inputs via its type signature, calls a service, and renders the result. **No business logic lives in tool functions.**
---
## Tech Stack
| Technology | Why |
|---|---|
| **Python 3.12+** | Modern typing, `datetime.fromisoformat`, enums, dataclasses. |
| **MCP Python SDK v2** (`mcp>=2,<3`) | The current stable SDK line. Its `MCPServer` (formerly `FastMCP`) generates JSON Schema from type hints, speaks stdio + Streamable HTTP, serves both protocol eras, and enables in-memory `Client(server)` testing. |
| **httpx** | Modern async/requests-compatible HTTP client with rich error types (`TimeoutException`, `TransportError`) that map cleanly onto our exception hierarchy. |
| **Pydantic v2** | Input validation and typed, serializable output models. |
| **SQLAlchemy 2.0** | Declarative ORM with type-safe `Mapped` columns, CHECK constraints, partial indexes — and a painless future migration path to PostgreSQL. |
| **SQLite** | Zero-config, single-file, perfect for a personal tool. Not a production multi-user database — see [Limitations](#limitations). |
| **python-dotenv** | `.env` loading (real environment variables still win). |
| **pytest + respx + pytest-asyncio** | Deterministic unit tests; `respx` mocks every GitHub HTTP call; `pytest-asyncio` drives the in-memory MCP client tests. |
---
## Installation
Requirements: **Python 3.12+** and **git**. (The MCP SDK itself needs ≥3.10; this project targets 3.12.)
### Windows (PowerShell)
```powershell
cd "C:\Users\ASUS\mcp project"
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
pip install -r requirements.txt
```
If `Activate.ps1` is blocked by the execution policy:
```powershell
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
```
### macOS / Linux
```bash
cd mcp-project
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
pip install -r requirements.txt
```
---
## Configuration
Copy the placeholder file and fill in your values:
```bash
cp .env.example .env # Windows: copy .env.example .env
```
| Variable | Meaning | Example |
|---|---|---|
| `GITHUB_TOKEN` | Fine-grained PAT with **Issues: Read & Write** on your repos. | `github_pat_...` |
| `LMS_PROVIDER` | Only `mock` is implemented today. | `mock` |
| `LMS_SEED_FILE` | Optional JSON seed file for the mock LMS. | *(leave unset)* |
| `DATABASE_PATH` | SQLite location (relative to project root). | `data/tasks.db` |
| `LOG_LEVEL` | `DEBUG`, `INFO`, `WARNING`, `ERROR`. | `INFO` |
| `GITHUB_BASE_URL` | GitHub API base. Leave default. | `https://api.github.com` |
| `REQUEST_TIMEOUT_SECONDS` | Outbound timeout. | `15.0` |
| `MAX_RESULTS` | Max issues per request. | `100` |
**Creating a GitHub token** → *GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token* → select only the repositories you need → grant only **Issues: Read and Write**.
> ⚠️ `.env` is git-ignored. Never commit it. `.env.example` contains placeholders only.
---
## Running the Server
### 1. Initialize the database and seed demo data
```bash
python -m scripts.seed_demo
```
This creates `data/tasks.db` and inserts a few realistic demo tasks (one deliberately overdue).
### 2. Run the MCP server
```bash
python -m app.server
```
The server launches over **stdio** (the default for desktop MCP clients) and stays running until stopped.
### Development & debugging
The SDK ships a CLI and an interactive inspector:
```bash
mcp dev app/server.py # launch + open the MCP Inspector in a browser
mcp run app/server.py # run the server (same behavior as python -m app.server)
```
---
## Connecting an AI Client
Local MCP servers run over stdio: the AI client launches your server process and speaks to it over stdin/stdout. Configuration format is the client's `mcpServers` block.
### Claude Desktop (Windows)
Edit `%APPDATA%\Claude\claude_desktop_config.json` (open via *Settings → Developer → Edit Config*), fully quit, and relaunch:
```json
{
"mcpServers": {
"ai-student-assistant": {
"command": "C:\\Users\\ASUS\\mcp project\\.venv\\Scripts\\python.exe",
"args": ["C:\\Users\\ASUS\\mcp project\\app\\server.py"]
}
}
}
```
Requirements:
- **Absolute paths** — Claude Desktop does not inherit your shell PATH or working directory.
- Use `where python` / `where git` to confirm the exact interpreter path.
- After saving, **fully restart** Claude Desktop, then look under the connectors/message-box menu for the server and its tools.
- Logs if it fails: `%APPDATA%\Claude\logs\mcp*.log`.
### Alternatives
- **MCP Inspector** (no config): `mcp dev app/server.py` gives you a GUI to call every tool by hand — ideal for demos.
- **Cursor** — `.cursor/mcp.json` uses the identical `mcpServers` shape.
- The server is transport-agnostic: the same `MCPServer` can be served over Streamable HTTP later (see [Future Improvements](#future-improvements)).
---
## Example Usage
> **User:** What GitHub issues are currently open?
The agent calls `get_open_issues` (no repository → issues assigned to you), then summarizes:
> *You have 2 open issues assigned to you: "Fix login bug" (#1, bug) and "Add CI pipeline" (#2).*
> **User:** What assignments are due in the next 7 days?
The agent calls `get_upcoming_deadlines` with `start`/`end` computed from today:
> *Due this week: Quiz 3 (Math, Aug 12), Project Proposal Draft (ENG101, Aug 11), Graph Traversal Assignment (CS101, Aug 13).*
> **User:** Create tasks for those assignments.
The agent calls `create_tasks_from_deadlines` (the server already honors `source`/`source_id` dedup, so re-running never duplicates):
> *Created 3 tasks. Skipped 0 (no duplicates).*
> **User:** Which tasks should I work on first?
The agent calls `get_workload_summary` and `get_overdue_tasks`, then reasons over priority + due dates:
> *First: "Fix flaky test in the CI pipeline" (OVERDUE, high). Then: Project Proposal Draft (due tomorrow), Quiz 3 (due in 2 days)...*
---
## API Integration
### GitHub
- **Endpoints:** `GET /issues` (assigned to you), `GET|POST /repos/{owner}/{repo}/issues`, `GET|PATCH /repos/{owner}/{repo}/issues/{number}`.
- **Auth:** `Authorization: Bearer <GITHUB_TOKEN>`. Anonymous access is allowed for public repos; a 401 then returns a clear "auth required" error.
- **Rate limits:** mapped 403-with-`x-ratelimit-remaining: 0` and 429 to a `rate_limited` error.
- **Pull requests:** the issues endpoint also returns PRs; they are filtered out via the `pull_request` key.
- All network/timeout/error states are translated into domain exceptions (see [Security](#security)).
### LMS
No legitimate/accessible college LMS API was assumed for this project, so the LMS lives behind a tiny interface (`LMSService`) with a realistic **mock** implementation (`MockLMSService`) that:
- seeds a course catalog with due dates *relative to today*,
- validates courses (unknown course → `not_found`),
- validates dates and ranges (bad input → `invalid_input`).
Adding a real provider later = implement the same interface + set `LMS_PROVIDER=real`. **No protected pages were scraped; nothing bypasses authentication.** The mock behaves like a real service so the rest of the app is tested without modification.
---
## Database
SQLite file at `DATABASE_PATH` (default `data/tasks.db`), one table in MVP:
```sql
CREATE TABLE tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT,
status TEXT NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending','completed')),
priority TEXT NOT NULL DEFAULT 'medium'
CHECK (priority IN ('low','medium','high','urgent')),
due_date TEXT, -- ISO-8601 (date or timestamp)
source TEXT, -- 'github' | 'lms' | NULL
source_id TEXT, -- e.g. GitHub issue number
source_url TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX idx_tasks_status ON tasks(status);
CREATE INDEX idx_tasks_due_date ON tasks(due_date);
CREATE INDEX idx_tasks_priority ON tasks(priority, due_date);
CREATE UNIQUE INDEX uq_tasks_source ON tasks(source, source_id)
WHERE source IS NOT NULL AND source_id IS NOT NULL;
```
**Why each decision matters:**
- **Partial unique index on `(source, source_id)`** — SQLite treats `NULL`s as distinct in a regular `UNIQUE`, which would let duplicate imports slip in *and* forbid multiple "personal" (un-sourced) tasks. The `WHERE source IS NOT NULL` partial index makes imports idempotent at the database layer, exactly where it belongs. This is what makes `create_task_from_issue` / `create_tasks_from_deadlines` safe to call repeatedly.
- **`status`/`priority` as TEXT + CHECK** — SQLite has no enums; the CHECK provides integrity while Python `enum.Enum`s mirror the values for type safety.
- **ISO-8601 UTC timestamps as sortable strings** — lexicographic ordering == chronological ordering, no timezone ambiguity, JSON-friendly.
- **`source` + `source_id` + `source_url`** preserve provenance so a task can always be traced back to the issue or assignment it came from.
---
## Testing
```bash
pytest # runs the whole suite: mocked GitHub, mock LMS, SQLite tasks, MCP client
```
Scope (`tests/`):
| File | Covers |
|---|---|
| `test_github_service.py` | Success + auth header, no-token mode, 401, 403 (auth vs. rate-limit), 404, malformed JSON, network failure, timeout, 5xx, PR filtering, create payload, invalid inputs — all via `respx`. |
| `test_lms_service.py` | Deadline listing, date-range + course filters, invalid course, bad dates, out-of-order range, assignment lookup, simulated upstream failure. |
| `test_task_service.py` | CRUD, filters, overdue detection (incl. completed-tasks-excluded), duplicate prevention, issue→task, deadlines→tasks, idempotency. |
| `test_mcp_tools.py` | In-memory MCP `Client(server)` — tool registration (all 15), valid & invalid inputs, structured error responses, and cross-service workflows end-to-end. |
MCP tools are tested against a real protocol connection using the SDK's **in-memory client** (`async with Client(server)`) — the same pattern as FastAPI's `TestClient`. No subprocess, no port, no credentials.
---
## Security
- **Secrets live only in environment variables** (`.env` is git-ignored; `.env.example` has placeholders).
- **Least privilege:** a fine-grained GitHub PAT limited to Issures **Read & Write** on specific repos — never full `repo` scope.
- **No secret logging:** a redacting filter scrubs `Authorization` values from logs; and since the server prints nothing on stdout (logging goes to **stderr**), the stdio protocol stream stays clean.
- **Input validation:** Pydantic at the tool boundary + domain validation in services.
- **Parametrized SQL** via SQLAlchemy — no string-built queries.
- **Controlled error exposure:** the AI gets structured errors (`code`, `message`); raw stack traces go only to server logs.
- **Minimum client exposure:** the `.env` token is read by the server process, not passed through client config.
See also the interview-focused discussion in [Interview Talking Points](#interview-talking-points).
---
## Limitations
Honest caveats, on purpose:
- **LMS is mocked.** `LMS_PROVIDER=mock` is the only provider. A real API adapter, exported calendar, or other authorized data source must be added to replace it (interchangeably, via the `LMSService` interface).
- **SQLite is single-user.** No concurrency guarantees, no network access, no backend replication. Deliberate for a personal assistant.
- **No OAuth / no HTTP transport yet.** The GitHub token is a static secret; the server runs over stdio. Fine for local personal use; remote/hosted use would need OAuth and Streamable HTTP.
- **Issue creation does not support assigning or body markdown explicitly** beyond free-text — kept intentionally small.
- **Single-hour granularity on deadlines** — no timezone conversion; dates are compared in user-provided ISO-8601 form.
- **Imports describe mutable source items as snapshots**: if a GitHub issue is later edited, an already-created task is not updated (a designed behavior, not a bug).
---
## Future Improvements
- Real `LMSService` adapter (official API or `.ics` calendar export)
- Google Calendar integration for deadlines
- Slack/Teams notifications for overdue tasks
- PostgreSQL backend (repository already abstracts this)
- OAuth for GitHub + Streamable HTTP transport + Docker deployment
- Task history/audit table; issue updates re-sync into tasks
- Richer agent workflows (auto-triage, weekly "standup" report resource)
---
## Project Architecture
**Layers, in dependency order:**
```
app/tools MCP adapters — type-hinted params, docstrings as descriptions, guard() → {ok, data, error}
app/services GitHubService · LMSService (mock) · TaskService — business logic + cross-service workflows
app/database Database (engine/session) · TaskRepository (all SQL)
app/models SQLAlchemy ORM (Task) · Pydantic schemas (TaskCreate/Out, GitHubIssue, Deadline)
app/config.py validated env config
app/exceptions domain error hierarchy → AI-readable codes
```
**Dependency injection:** `app/server.py` is the composition root — it builds config → database → services → `MCPServer`, and *registers* tool functions with the services they need. Nothing is a global; tests assemble the same graph with fakes.
**Error flow:** tool → service → repository/API raises a `StudentAssistantError` → `guard()` renders `{ok: false, error: {code, message}}`. Unexpected exceptions are logged (stderr) and returned as a generic `internal_error` message.
---
## Demo Scenario
1. Create a fine-grained GitHub token and set `GITHUB_TOKEN` in your `.env`.
2. Seed the task database: `python -m scripts.seed_demo` (creates a few tasks, one overdue).
3. Start the server: `python -m app.server` (or `mcp dev app/server.py` to launch the Inspector).
4. Connect Claude Desktop / Inspector to the server.
5. Ask: **"What do I need to work on this week?"**
→ the agent calls `get_workload_summary`, combines open GitHub issues + upcoming deadlines + pending/overdue tasks, and gives a prioritized answer.
6. Ask: **"Create tasks for all assignments due this week."**
→ the agent calls `create_tasks_from_deadlines`.
7. Verify in the database:
```bash
sqlite3 data/tasks.db "SELECT title, due_date, source FROM tasks ORDER BY due_date;"
```
→ new rows appear with `source = 'lms'`, one per deadline. Re-run the same question and the tool reports `skipped` instead of duplicating.
---
## Interview Talking Points
Be ready to defend these decisions:
1. **Why MCP?** It's a standardized protocol so one server works with *any* AI client; tools are discovered (`tools/list`), called (`tools/call`), and described to the model — naming and descriptions are a UX contract for LLMs.
2. **Why the current MCP SDK v2?** The SDK renamed `FastMCP` → `MCPServer` and now serves both the 2025 and 2026-07-28 protocol revisions from one process; `pip install mcp` installs v2. Building on the maintained line (not v1 maintenance) is the defensible choice.
3. **Thin MCP layer / service layer.** Tool functions are adapters; logic lives in services behind interfaces. This is what makes GitHub, LMS, and tasks pluggable and testable without a network.
4. **Partial unique index for idempotency.** Explain *why* SQLite needs a partial index for `(source, source_id)` and how it makes `create_task_from_issue`/`create_tasks_from_deadlines` safe as a small, reasoned demo of SQL depth.
5. **GitHub 403 ambiguity.** Forbidden vs. rate-limited is disambiguated via the `x-ratelimit-remaining` response header — a real-world API-integration subtlety, not folklore.
6. **Least-privilege tokens.** Fine-grained PAT with only `Issues: Read & Write` vs. a classic `repo`-scope token. Know the "why" cold.
7. **Error taxonomy.** One exception hierarchy mapped to stable AI-readable codes, with stack traces confined to logs. Reliability is a design goal, not an afterthought.
8. **Testing the protocol layer.** In-memory `Client(server)` means the MCP wiring is tested exactly as a client uses it.
9. **Honest scoping.** The LMS is explicitly mocked; SQLite is single-user — "personal productivity tool", not a claim of an enterprise multi-user product.
---
## License
MIT — see [LICENSE](LICENSE). Copyright (c) 2026 Mahendra Vattikuti.This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues