Skip to main content
Glama
README.md
<div align="center">

# pw-mcp

**Physics Wallah Study MCP Server**

Schedule tracking, backlog management, and progress monitoring for PW students — powered by the Model Context Protocol.

[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![Tests](https://img.shields.io/badge/tests-78%20passing-brightgreen.svg)](#testing)

</div>

---

## What it does

Connect your AI assistant to your PW study data. Ask questions like:

- *"What classes do I have today?"*
- *"How much physics backlog do I have?"*
- *"Generate a 7-day study plan for 4 hours/day"*
- *"Mark the chemical bonding lecture as done"*

The server exposes **15 MCP tools** that any MCP-compatible client (Claude, opencode, etc.) can call.

---

## Quick Start

```bash
# Clone
git clone https://github.com/vspcoderz/pw-mcp.git
cd pw-mcp

# Install
uv venv && source .venv/bin/activate
uv sync

# Run (mock mode — no credentials needed)
uv run pw-mcp
```

That's it. Mock mode works offline with realistic sample data across Physics, Chemistry, and Mathematics.

---

## Real PW Integration

To connect to your actual PW account:

1. **Get your API token:**
   - Log into [pw.live](https://pw.live) in your browser
   - Open DevTools → Network tab
   - Filter by `token`, find the `verify-token` request
   - Copy the `Authorization: Bearer <token>` value

2. **Configure:**
   ```bash
   cp .env.example .env
   ```
   Edit `.env`:
   ```
   PW_PROVIDER=pw
   PW_API_TOKEN=your_token_here
   PW_DEFAULT_BATCH=your-batch-slug
   ```

3. **Run:**
   ```bash
   uv run pw-mcp
   ```

**How it works:**
- The real provider uses PW's `weekly-schedule` endpoint to fetch today's and upcoming lectures — the same data the PW app shows in the "Weekly Schedule" tab
- Falls back to the topics/contents endpoint for older lectures
- Progress tracking is stored locally in SQLite (PW doesn't expose a server-side progress API)
- Set `PW_DEFAULT_BATCH` to your batch slug (the part after `/batches/` in the PW URL)

---

## Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `PW_PROVIDER` | `mock` | `mock` (offline) or `pw` (real API) |
| `PW_API_TOKEN` | — | Bearer token for pw.live |
| `PW_API_BASE_URL` | — | Override API base URL |
| `PW_DEFAULT_BATCH` | `arjuna-jee-2027-243495` | Batch slug for schedule fetching |
| `PW_TIMEOUT` | `20` | Request timeout in seconds |
| `DATABASE_PATH` | `./data/pw_mcp.db` | SQLite database path |
| `TIMEZONE` | `Asia/Kolkata` | Timezone for schedule calculations |
| `LOG_LEVEL` | `INFO` | Logging verbosity |

---

## Architecture

```
┌─────────────────────────────────────────────┐
│              MCP Client                     │
│         (Claude, opencode, etc.)            │
└──────────────────┬──────────────────────────┘
                   │ stdio
┌──────────────────▼──────────────────────────┐
│            MCP Server (server.py)           │
├─────────────┬───────────────┬───────────────┤
│   Schedule  │    Backlog    │   Progress    │
│   Service   │    Service    │   Service     │
├─────────────┴───────────────┴───────────────┤
│            PWProvider (ABC)                 │
├──────────────────┬──────────────────────────┤
│  MockPWProvider  │    RealPWProvider        │
│   (offline)      │    (pw.live API)         │
└──────────────────┴──────────────────────────┘
```

**Design principle:** Services never know where data comes from. The provider abstraction makes swapping mock ↔ real trivial.

---

## MCP Tools

### Schedule

| Tool | Description |
|------|-------------|
| `get_today_schedule` | Today's lectures |
| `get_upcoming_schedule` | Next N days (1-30) |
| `get_schedule` | Custom date range |
| `get_next_lecture` | Next upcoming incomplete lecture |
| `get_lecture` | Single lecture by ID |

### Backlog

| Tool | Description |
|------|-------------|
| `get_backlog` | Incomplete past lectures (filterable) |
| `get_subject_backlog` | Backlog for a subject |
| `get_chapter_backlog` | Backlog for a chapter |
| `get_backlog_summary` | Aggregate statistics |
| `get_oldest_backlog_lecture` | Oldest incomplete lecture |

### Progress

| Tool | Description |
|------|-------------|
| `mark_lecture_completed` | Mark lecture done |
| `mark_lecture_incomplete` | Mark lecture not done |
| `update_watched_minutes` | Update watch progress |
| `get_completion_stats` | Overall progress |

### Planning

| Tool | Description |
|------|-------------|
| `generate_study_plan` | Personalized study plan from backlog |

---

## MCP Client Setup

### opencode

Add to your `opencode.json`:

```json
{
  "mcp": {
    "pw": {
      "type": "stdio",
      "command": "uv",
      "args": ["run", "--directory", "/path/to/pw-mcp", "pw-mcp"]
    }
  }
}
```

### Claude Desktop

Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "pw": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/pw-mcp", "pw-mcp"]
    }
  }
}
```

### MCP Inspector

Test with the official inspector:

```bash
npx @modelcontextprotocol/inspector uv run pw-mcp
```

---

## Testing

```bash
uv run pytest -v
```

78 tests covering:
- Schedule queries (today, upcoming, date range)
- Backlog calculation (subject/chapter filtering, summary)
- Progress tracking (mark complete/incomplete, watched minutes)
- SQLite persistence (upsert, delete, reconnection)
- Study plan generation (hours constraint, subject filter)
- Model validation and timezone handling
- Provider selection and configuration

---

## Project Structure

```
pw-mcp/
├── pyproject.toml
├── .env.example
├── src/pw_mcp/
│   ├── server.py           # MCP tool definitions + wiring
│   ├── config.py           # pydantic-settings config
│   ├── exceptions.py       # Custom exceptions
│   ├── models/             # Pydantic data models
│   │   ├── lecture.py
│   │   ├── schedule.py
│   │   ├── backlog.py
│   │   └── progress.py
│   ├── providers/          # Data source abstraction
│   │   ├── base.py         # PWProvider ABC
│   │   ├── mock.py         # Offline with sample data
│   │   └── pw.py           # Real pw.live API
│   ├── services/           # Business logic
│   │   ├── schedule.py
│   │   ├── backlog.py
│   │   ├── progress.py
│   │   └── study_plan.py
│   ├── storage/            # SQLite persistence
│   │   └── sqlite.py
│   └── utils/              # Date/formatting helpers
│       ├── dates.py
│       └── formatting.py
├── tests/                  # pytest suite
└── docs/
    └── pw-api.md           # PW API reverse-engineering docs
```

---

## PW API Notes

See [`docs/pw-api.md`](docs/pw-api.md) for full endpoint documentation.

**Key findings:**
- PW uses OTP-based auth (no username/password)
- Weekly schedule is available via `/v3/batches/{slug}/weekly-schedule`
- Progress tracking is client-side only
- Two platforms: `pw.live` (school/exam prep) and `pwskills.com` (professional courses)

---

## License

MIT

TDQS

B3.3/5.0

Scored across 15 tools

Disambiguation4/5

Tools are mostly distinct by resource and filter: schedule queries differ by time range, and backlog queries differ by subject/chapter/aggregate. Minor ambiguity exists between get_next_lecture and get_oldest_backlog_lecture, and get_backlog vs get_backlog_summary, but descriptions largely resolve this.

Naming Consistency5/5

All tool names follow a consistent verb-first pattern: get_, mark_, update_, generate_. Nouns are clear and singular, with no mixed casing or vague action verbs.

Tool Count4/5

15 tools is at the upper edge of well-scoped, and most tools serve a distinct purpose. A few convenience getters (today's schedule, upcoming schedule, date-range schedule) could be consolidated, but the count is still reasonable for the domain.

Completeness4/5

The surface covers schedule lookup, backlog filtering, completion state changes, watched minutes, stats, and study plan generation. Minor gaps like explicit subject/chapter listing or a way to view all lectures outside a date range are workable around.

Maintenance

ActivityMaintained
ResponsivenessNo issues