bb-mcp
by changshenhan
README.md
# bb-mcp
[](LICENSE)
[]()
[]()
A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for the
**CUHK-SZ Blackboard** instance (`bb.cuhk.edu.cn`, Blackboard Learn Classic 3900).
Let your AI assistant download courseware, inspect assignments, read announcements,
and submit homework — fully headless, no browser window ever pops up.
一个给 AI 助手用的中大(港中深)Blackboard MCP 工具:课件下载、作业查看与提交、公告阅读。
全程无浏览器窗口,凭据只存本机。中文说明见下方 [中文说明](#中文说明)。
> ⚠️ For personal productivity only. Use responsibly and follow your
> institution's IT policies. Don't hammer the server; don't automate anything
> that violates academic integrity rules.
## Tools (9)
| Tool | What it does |
|---|---|
| `bb_login()` | Headless login using `cred.json` (template auto-generated on first run) |
| `bb_status()` | Session check; auto re-login once with stored credentials if expired |
| `bb_list_courses()` | List all your courses |
| `bb_list_course_files(course)` | List every file in a course's content area (newest first) |
| `bb_download_courseware(course, keyword, latest)` | Download the latest courseware to `<output dir>/<course>/` |
| `bb_list_assignments(course)` | List assignment/test entry links (with visible dates) |
| `bb_assignment_details(course, keyword, download)` | Scan all content areas for assignments: summary, due date, submission-entry status, attachment download |
| `bb_announcements(course, limit)` | Read announcements (`course="all"` scans every course) |
| `bb_submit_assignment(course, assignment, file_path, confirm)` | Submit homework — **two-phase**: dry-run by default, `confirm=true` actually submits |
Every tool auto-re-authenticates when the session expires, and tells you
explicitly to update `cred.json` when your password has changed.
## Requirements
- Python **3.10+**
- A Claude Code / MCP-compatible client
- A `bb.cuhk.edu.cn` account
## Install
```bash
git clone https://github.com/changshenhan/bb-mcp.git
cd bb-mcp
python -m venv venv
# Windows
venv\Scripts\pip install -r requirements.txt
# macOS / Linux
venv/bin/pip install -r requirements.txt
# Install Chromium (goes to Playwright's default cache)
venv\Scripts\playwright install chromium
```
`requirements.txt` pins `mcp<2` (v2 renamed `FastMCP` and breaks the import)
plus `playwright`.
## Configure
**1. Credentials** — run any tool once and `cred.json` is generated next to
`server.py`. Fill in your values:
```json
{
"username": "your_student_id",
"password": "your_password"
}
```
The username may include `@link.cuhk.edu.cn` / `@cuhk.edu.cn` — the suffix is
stripped automatically, same as the site's own login JS. **The file stays on
your machine and is git-ignored; never commit or share it.**
**2. Register with your MCP client** (Claude Code example, see
`.mcp.json.example`):
```json
{
"mcpServers": {
"bb-cuhk": {
"command": "<absolute path to venv python>",
"args": ["<absolute path to>/bb-mcp/server.py"],
"env": {
"BB_OUTPUT_DIR": "C:\\path\\to\\your\\download\\folder"
}
}
}
}
```
**3. Optional environment variables**
| Variable | Default | Purpose |
|---|---|---|
| `BB_OUTPUT_DIR` | `~/Desktop/BB-Courseware` | Download root for courseware & attachments |
| `PLAYWRIGHT_BROWSERS_PATH` | Playwright default cache | Custom Chromium install location |
| `BB_BASE_URL` | `https://bb.cuhk.edu.cn` | Point at another Blackboard instance (see below) |
## Usage examples
Just talk to your assistant:
- *"Download the newest slide from my Computer Networks course"*
→ `bb_download_courseware(course="Networks", latest=1)`
- *"What assignments are due in the Graph Computing course?"*
→ `bb_assignment_details(course="Graph")`
- *"Check announcements for all courses"*
→ `bb_announcements(course="all")`
- *"Submit answer.md to the Topological Sort assignment"*
→ dry-run first, then `confirm=true`
## How the login works (for hackers)
The site's custom login page (a Blackboard `bb-SSOIntegrationDemo` building
block) obfuscates its client-side crypto. Reversed, it is simply:
```
check(): password field ← base64(pw)
validate_form(): encoded_pw = base64(base64(pw))
encoded_pw_unicode = base64(utf16le(base64(pw))) # md5.js chrsz=16
password field ← ""
POST /webapps/bb-SSOIntegrationDemo-BBLEARN/execute/authValidate/customLogin?returnUrl=…
fields: user_id, tstring (from page), pstring, encoded_pw, encoded_pw_unicode,
action=login, one_time_token=false, ...
```
Two quirks worth knowing if you adapt this:
- **The site rejects plain Python OpenSSL TLS handshakes** (curl works fine, so
it is client-fingerprinting). All HTTP therefore goes through Playwright's
`APIRequestContext` — the browser's own TLS stack — which also shares the
cookie jar with the DOM session.
- **The failure page shows no error text at all** (302 back to `/`, clean login
form). Login success is therefore decided by a positive check: opening the
portal tab and verifying it does *not* bounce to `/webapps/login`.
Porting to another school: flip `BB_BASE_URL` and try. If your instance uses
the same custom-login building block it will work as-is; if it redirects to
ADFS/Shibboleth you'll need to rewrite `_http_login()` against your login
page's form fields.
## Download dedup rules
- Filename comes from `content-disposition`; when missing, the content item's
display name + a content-type-derived extension is used.
- A file is skipped when an identical filename (case-insensitive) **or** an
identical byte size (>1 KB) already exists under the output root — manually
renamed copies from previous downloads are recognised by size.
## Self-test
```bash
python _mcp_selftest.py
# → handshake ok, tools: [... 9 tools ...]
```
## Known limits
- Built for Blackboard **Classic** (JSP pages). If your school switches to
Ultra, `_collect_files` and the submission selectors need a rewrite.
- File dates are scraped from `YYYY-MM-DD` text near the content item;
undated items sort last.
- The submission receipt is a page-text excerpt — always eyeball it after
submitting.
## Security
- Credentials live only in `./cred.json` (git-ignored), sessions only in
`./profile/` (git-ignored). Nothing is sent anywhere except
`bb.cuhk.edu.cn` itself.
- `bb_submit_assignment` is two-phase by design: the default `confirm=false`
call only opens the attempt page and returns an excerpt; nothing is
submitted until you pass `confirm=true`.
## 中文说明
1. `pip install -r requirements.txt` 后执行 `playwright install chromium`
2. 首次运行任意工具,目录里会自动生成 `cred.json`,填入学号密码保存
(带不带 `@link.cuhk.edu.cn` 后缀都行)
3. 把 `.mcp.json.example` 抄进你项目根的 `.mcp.json`,路径改成你的 venv
Python 和本目录 `server.py` 的绝对路径
4. 之后对 Claude 说人话即可:「把网络课最新课件下下来」「看看有哪些作业待交」
5. 改了学校密码?工具会明确提示「登录失败」,把新密码填回 `cred.json` 再跑
下载默认落到 `~/Desktop/BB-Courseware/<课程名>/`,作业附件在 `<课程名>/assignments/`,
可用 `BB_OUTPUT_DIR` 环境变量改。
## License
[MIT](LICENSE)
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues