polyu-estudent-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@polyu-estudent-mcpWhat's my GPA?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
eStudent MCP
English | 中文
A local MCP server that lets Claude Code operate the PolyU eStudent portal — check grades, timetable and exams, search subjects, and (work in progress) add/drop & snipe courses.
Everything runs on your own machine. Your NetID and password never leave it.
⚠️ This is an unofficial personal tool, not affiliated with PolyU. Use it on your own account and at your own risk, and respect the university's acceptable-use policy.
✨ Features & Status
Tool | What it does | Status |
| Is there a live logged-in session? | ✅ Live-verified |
| Grades & GPA, optional | ✅ Live-verified |
| Class timetable | ✅ Live-verified |
| Exam dates / venues / seats | ✅ Live-verified |
| Search by code/keyword or by programme; auto term, pagination, per-group vacancy on a single match | ✅ Live-verified |
| Preview add/drop — never submits | ⏳ Logic done; live calibration pending |
| Submit add/drop (needs preview | ⏳ Pending registration window |
| Automated grab (open-time / vacancy-watch) | ⏳ Scheduler done; real submit pending |
| List / inspect / stop sniper jobs | ✅ Works |
Done & verified
Login via PolyU ADFS SSO (NetID + password, no 2FA for the tested account); session is persisted and reused.
Read tools (
get_grades/get_timetable/get_exam_schedule) calibrated against the live portal.search_subjects— both by subject (code/title) and by programme (department → programme cascade); tries each term automatically when none is given; walks the result pagination to collect all subjects; when the query resolves to exactly one subject it drills into the detail page to return each teaching group's vacancy (handles5open /(4)reserved /W=.. Top-up vac=..waitlist).Two-step registration invariant, sniper frequency floors, the sniper's self-healing retry logic (auto re-login, browser relaunch, open-time → vacancy-watch fallback), and all parsers are unit-tested (27 tests).
Not finished yet
preview_registration/confirm_registrationreal submission, and the sniper's real grab path, can only be calibrated when the subject registration window is open (it was closed during development). The scheduling, frequency safety, and preview→confirm fingerprint logic are already built and tested.search_subjectskeyword results across many pages are de-duplicated but large title searches return the offered subjects only (no extra filters yet).
Related MCP server: SBU Syllabus MCP
🚀 Installation
Prerequisites: macOS, Python 3.12, uv,
and Claude Code.
# 1. Clone
git clone https://github.com/<your-account>/polyu-estudent-mcp.git
cd polyu-estudent-mcp
# 2. Create the venv and install deps
uv sync --extra dev
# 3. Install the headless browser Playwright drives
.venv/bin/python -m playwright install chromium
# 4. Configure your credentials
cp .env.example .env
# then edit .env — see the next sectionConfigure .env
.env is git-ignored and must contain your real credentials. It never gets
committed or pushed.
ESTUDENT_NETID=your_netid # e.g. 12345678d
ESTUDENT_PASSWORD=your_password # your portal password
ESTUDENT_BASE_URL=https://www.polyu.edu.hk/student
ESTUDENT_HEADFUL=0 # 1 = show the browser (debugging)chmod 600 .env # restrict to your userVerify
uv run pytest -q # 27 tests should pass🔌 Register with Claude Code
claude mcp add estudent -s user -- \
"$(pwd)/.venv/bin/python" -m estudent_mcp.server-s user registers it globally (available in every project). Drop it for the
current project only. Confirm it connected:
claude mcp get estudent # should show ✔ Connected💬 Usage
Just talk to Claude Code in natural language — it picks the right tool.
You say | Tool used |
"What's my GPA?" |
|
"Show my timetable" |
|
"When are my exams?" |
|
"Find COMP1011 and its vacancy" |
|
"Search programmes in COMP for BA(HONS) COMPUTING" |
|
"Watch COMP2012 for a vacancy" |
|
Two-step registration
confirm_registration only submits if you pass the fingerprint returned by a
preview_registration of the same actions — so you can never add/drop a
subject without first seeing exactly what will change.
The sniper is deliberately restrained
High-frequency "bombing" polling is not supported, by design — it's the fastest way to get rate-limited/banned (so you'd miss the course anyway), may violate the acceptable-use policy, and is unfair to others.
Frequency floors are enforced (sub-floor configs are rejected):
open_time mode: retry ≥ 3s, intense window ≤ 30 min.
watch_vacancy mode: poll ≥ 30s.
Note the real rate limiter is the attempt itself — each one is a full browser flow taking several seconds — so "every 3s" works out to roughly 4–6 attempts per minute, comparable to a fast human refreshing.
Within those floors, jobs are self-healing and built to survive the
launch-day crush: during the open window page timeouts shrink to ~12s so a
crashed portal costs seconds per probe, an expired session re-authenticates
automatically with your .env credentials, and a crashed browser is closed
and relaunched. Errors are classified: portal unreachable (timeouts,
connection errors — expected while the portal is being hammered) is judged by
duration and only ends the job after 30 minutes of continuous downtime;
page-structure errors (portal redesign) end it after 5 strikes; rejected
credentials end it immediately. Pass then_watch=true to open_time so an
unsuccessful open window falls through to vacancy watching instead of giving
up. Terminal events (grabbed / fallback / failed) raise a native macOS
notification.
Keep your Mac awake while a job runs — e.g. caffeinate -dims in a spare
terminal. Set ESTUDENT_HEADFUL=1 to watch the browser.
🔒 Security
.env(your credentials),.runtime/(saved session + any captured page HTML, which may contain personal data), and screenshots are all git-ignored and were never committed to history.Only
.env.example(a placeholder template) is tracked.Credentials are read locally by
config.pyand used solely to log into eStudent from your machine.
If you fork/clone, never commit your .env.
🏗 Architecture
src/estudent_mcp/
server.py # MCP tool layer (no browser code)
backend/
base.py # EStudentBackend interface (stable)
playwright_backend.py # scheme A: headless browser (current)
# future: hybrid_backend.py — scheme C: Playwright login + httpx fetch
parsers/ # HTML -> dataclasses (pure, unit-tested)
registration.py # fingerprint + summary (two-step invariant)
sniper.py # scheduler + frequency floors
models.py config.py errors.py
tests/ # parser / registration / sniper unit tests
scripts/ # joint-debug probes (no PII committed)The EStudentBackend interface lets a faster HTTP backend (scheme C) be swapped
in later without touching the tools or parsers.
🛠 Why a browser instead of an API?
eStudent exposes no public API, so the backend drives a real headless Chromium (via Playwright) that logs in and reads pages exactly like a human would, then parses the HTML tables into structured data.
It passes login because PolyU's ADFS presented no CAPTCHA/2FA for the tested
account and a real browser is used — not by defeating any verification. If
the school adds a CAPTCHA/2FA, the right move is headful mode (ESTUDENT_HEADFUL=1)
to solve it once and reuse the session.
📄 License
Personal use. No warranty.
中文文档
English | 中文
一个本地 MCP 服务器,让 Claude Code 帮你操作 香港理工大学 eStudent 门户:查成绩、课表、考试,搜课,以及(开发中)选课/退课 与抢课。
所有操作都在你本机运行,账号密码绝不外传。
⚠️ 本项目为非官方个人工具,与理大无关。仅用于你自己的账号,风险自负,并请遵守学校的 网络使用规定。
✨ 功能与完成度
工具 | 功能 | 状态 |
| 是否有有效登录会话 | ✅ 已实测 |
| 成绩与 GPA,可按学期过滤 | ✅ 已实测 |
| 上课时间表 | ✅ 已实测 |
| 考试日期、地点、座位 | ✅ 已实测 |
| 按代码/关键词、或按专业搜课;自动选学期、自动翻页、单科目下钻名额 | ✅ 已实测 |
| 预演选退课,不提交 | ⏳ 逻辑完成,待联调 |
| 提交选退课(需预演指纹) | ⏳ 待选课窗口开放 |
| 定时抢课(开放瞬间 / 满员捡漏) | ⏳ 调度完成,真实提交待接通 |
| 查看、检查、停止抢课任务 | ✅ 可用 |
已完成并验证
登录走理大 ADFS 单点登录(账号密码,测试账号无 2FA),会话持久化复用。
读取类工具(
get_grades/get_timetable/get_exam_schedule)已对真实门户校准。search_subjects支持按科目(代码/标题)和按专业(院系 → 专业级联)两种 模式;无学期时自动逐学期尝试;自动翻页收齐全部科目;命中单个科目时下钻详情页返回每个 教学组的名额(识别5开放 /(4)保留 /W=.. Top-up vac=..候补)。两步选课确认、抢课频率下限、抢课自愈重试逻辑(自动重登、浏览器重启、 开抢失败转捡漏)、所有解析器均有单测(27 个)。
尚未完成
选课/退课的真实提交(
preview_registration/confirm_registration)、抢课真实下单, 必须等选课窗口开放才能联调(开发期间未开放)。调度、频率保护、预演→确认指纹逻辑 已写好并测试。关键词大范围搜索已去重,但暂未加更多筛选条件。
🚀 安装
前置要求:macOS、Python 3.12、uv、
Claude Code。
# 1. 克隆
git clone https://github.com/<your-account>/polyu-estudent-mcp.git
cd polyu-estudent-mcp
# 2. 创建虚拟环境并装依赖
uv sync --extra dev
# 3. 安装 Playwright 用的浏览器
.venv/bin/python -m playwright install chromium
# 4. 配置凭据
cp .env.example .env
# 然后编辑 .env —— 见下一节配置 .env
.env 已被 git 忽略,填你的真实凭据,绝不会被提交或推送。
ESTUDENT_NETID=your_netid # 例如 12345678d
ESTUDENT_PASSWORD=your_password # 门户密码
ESTUDENT_BASE_URL=https://www.polyu.edu.hk/student
ESTUDENT_HEADFUL=0 # 1 = 显示浏览器(调试用)chmod 600 .env # 仅本人可读写验证
uv run pytest -q # 应有 27 个测试通过🔌 注册到 Claude Code
claude mcp add estudent -s user -- \
"$(pwd)/.venv/bin/python" -m estudent_mcp.server-s user 是全局注册(所有项目可用),去掉则只对当前项目生效。确认连接:
claude mcp get estudent # 应显示 ✔ Connected💬 使用
直接用自然语言跟 Claude Code 说,它会自动选用合适的工具。
你说 | 用到的工具 |
"查一下我的成绩和 GPA" |
|
"这学期课表" |
|
"考试安排" |
|
"搜一下 COMP1011 还有没有名额" |
|
"按专业搜 COMP 系 BA(HONS) COMPUTING 的课" |
|
"帮我盯着 COMP2012 有没有人退课" |
|
两步选课确认
confirm_registration 只有在你回传同一组操作的 preview_registration 指纹时才会
真正提交——确保你在提交前一定先看清会发生什么。
抢课刻意克制
高频"轰炸式"轮询不支持:那是被限速/封号最快的方式(反而抢不到),可能违反学校规定, 也对他人不公平。
强制频率下限(低于下限直接拒绝):
open_time 模式:重试间隔 ≥ 3 秒,密集抢课窗口 ≤ 30 分钟。
watch_vacancy 模式:轮询间隔 ≥ 30 秒。
注意真正的限速器是尝试本身——每次都是一整套浏览器操作、耗时数秒,所以"每 3 秒 一次"实际约为每分钟 4~6 次,和一个手快的人不停刷新相当。
在下限之内,任务具备自愈能力,专为开抢瞬间系统被挤崩的场景设计:开抢窗口
内页面超时压缩到约 12 秒(崩溃时每次探测只花几秒而不是干等 45 秒);登录过期
自动用 .env 凭据重登;浏览器崩溃自动关闭重启。错误分类处理:门户不可达
(超时、连接错误——开抢拥挤期的常态)按持续时长判定,连续宕机 30 分钟才放弃;
页面结构错误(门户改版)连续 5 次终止;密码被拒立即终止。open_time 模式可传
then_watch=true:开抢窗口没抢到时自动转入捡漏轮询而不是放弃。
抢到 / 转捡漏 / 失败等终态会弹出 macOS 系统通知。
任务运行时让 Mac 保持唤醒——可在另一个终端跑 caffeinate -dims。
设 ESTUDENT_HEADFUL=1 可看到浏览器。
🔒 安全
.env(凭据)、.runtime/(会话与抓取的页面 HTML,可能含个人数据)、截图全部被 git 忽略,且从未进入提交历史。仓库里只跟踪
.env.example(占位模板)。凭据由
config.py在本地读取,只用于从你本机登录 eStudent。
如果你 fork/clone,切勿提交你的 .env。
🏗 架构
src/estudent_mcp/
server.py # 工具层,不碰浏览器
backend/
base.py # EStudentBackend 稳定接口
playwright_backend.py # 方案A:无头浏览器(当前)
# future: hybrid_backend.py — 方案C:Playwright 登录 + httpx 取数(预留)
parsers/ # HTML -> dataclasses,纯函数可单测
registration.py # 指纹 + 摘要(两步确认)
sniper.py # 调度 + 频率下限
models.py config.py errors.py
tests/ # 解析/选课/抢课单元测试
scripts/ # 联调探测脚本(不含个人数据)EStudentBackend 接口让以后可无痛换成更快的 HTTP 后端(方案 C),不动工具层和解析层。
🛠 为什么用浏览器而非 API?
eStudent 没有公开 API,所以后端用 Playwright 驱动一个真实的无头 Chromium,像人一样 登录、读页面,再把 HTML 表格解析成结构化数据。
它能登录,是因为测试账号的 ADFS 没有验证码/2FA、且用的是真实浏览器——不是靠破解
验证。若学校加了验证码/2FA,正确做法是用有头模式(ESTUDENT_HEADFUL=1)手动过一次、
复用会话。
📄 许可
个人使用,不提供任何担保。
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/1708004874a-star/polyu-estudent-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server