sio-mcp
by noah-lowry
README.md
## foreword
this is a proof of concept by claude and not suitable for production
-noah
# sio-mcp
Read-only MCP server over [CMU Student Information Online](https://s3.andrew.cmu.edu/sio/mpa/) (SIO), built as a thin wrapper around a reusable typed client library.
## Layout
```
src/sio_mcp/
utils/session.py auth: Playwright login (AndrewID + Duo), cookie persistence
terms.py term codes (F26 <-> fall-2026 <-> "Fall 2026")
models.py pydantic models; ISO dates, 24h times, decimal-string money
parsers/ HTML parsers for SIO's server-rendered pages
client.py SIOClient: typed async API with TTL caching
server.py the MCP server (8 read-only tools)
cli.py `sio-login`
tests/fixtures/ real SIO page snapshots the parser tests run against
```
`SIOClient` is the reuse surface: the MCP server, a CLI, a cron job, or a RAG
ingestion script all call the same functions.
## Setup
```sh
uv sync
uv run sio-login # AndrewID + password (env: ANDREW_ID/ANDREW_PASSWORD or prompts), approve the Duo push
```
Sessions expire on CMU's schedule; when a tool reports an authentication
error, re-run `sio-login`.
Register with Claude Code:
```sh
claude mcp add cmu-sio -- uv --directory /path/to/this/repo run sio-mcp
```
## Tools
| Tool | Returns |
|---|---|
| `get_student_context` | name, current term, class level, major, advisor, units |
| `get_schedule` | sections with days/times/rooms/instructors + add/drop deadlines |
| `get_schedule_ics` | official `.ics` export (authoritative semester dates) |
| `get_grades` | per-course grades + QPA for a semester |
| `get_advanced_placement` | AP/transfer credit and exam scores |
| `get_enrollment_status` | enrollment record for a semester |
| `get_account_activity` | student account ledger + balance |
| `get_campus_life` | housing assignment, Plaid Cash |
Conventions: times are `America/New_York` `HH:MM`; dates ISO `YYYY-MM-DD`;
money is a decimal string in USD (charges positive, payments/credits
negative); course codes are normalized to `15-122` form; `term` params accept
`F26`, `fall-2026`, or `Fall 2026`.
## Testing
```sh
uv run pytest
```
Parser tests run offline against `tests/fixtures/`. SIO redeploys weekly — if
a parser starts raising `UpstreamChangedError`, re-capture the page into the
fixtures and adjust.
## Non-goals (for now)
- **Writes.** Registration/drop actions live in SIO's legacy GWT app behind
opaque GWT-RPC, and SIO explicitly warns that automated registration
requests get access revoked. This server is deliberately read-only.
- The legacy `/sio/#...` app generally (invoices, pay-now, planning). The data
worth reading is duplicated on the server-rendered pages this uses.
TDQS
A4.4/5.0
Scored across 8 tools
Disambiguation4/5
Each tool targets a distinct data domain (schedule, grades, billing, etc.), but get_student_context and get_enrollment_status overlap on class level, major, and advisor. The descriptions clarify when to use which, so confusion is unlikely.
Naming Consistency5/5
Every tool follows the exact same get_<domain> pattern with clear, descriptive nouns. This is perfectly consistent and predictable.
Tool Count5/5
Eight tools is well within the ideal range for a student data server. Each tool covers a meaningful data area without redundancy or bloat.
Completeness5/5
The server covers the full read-only student information surface: context, schedule, grades, credits, enrollment, billing, and campus life. No obvious missing queries for the stated purpose.
Maintenance
ActivityMaintained
ResponsivenessSyncing