Show a desktop notification when the AI TA finishes replying
Notify me when classmates post messages in the forum
Play an alert sound whenever there is a new notification
Using Anthropic's Model Context Protocol as the interface, package Uedu Public API v1 into tools that an LLM can call directly. Supports stdio and streamable-http dual transport, SHA-256 hashed keys, and two-tier rate limiting, strictly separating public and sensitive data.
University teachers and researchers often need to query platform data ("What courses is NCU General Education Centre offering in 114-2?" "Which papers has the Uedu team published this year?"), but are not familiar with REST APIs and do not want to write Python scripts. The previous solution was to build many query pages, but UI development costs were high and maintenance was difficult.
Model Context Protocol (MCP) is an open protocol proposed by Anthropic in 2024, allowing LLM clients (Claude Desktop, Claude Code, Cursor, VS Code, etc.) to access external data via a standardised tool interface. Uedu wraps Public API v1 as an MCP server, enabling researchers to query data in natural language and allowing the LLM to decide autonomously which tool to call.
This is the infrastructure behind Uedu's design philosophy of Human-AI Collaboration. Without writing any code, researchers can turn AI assistants such as Claude and Cursor into research assistants familiar with Uedu data. This lowers the barrier for scholars without an engineering background to use the platform's data.
The MCP Server is not an independent data source, but a wrapper layer for Public API v1 (/api/v1/*):
LLM Client (Claude/Cursor/VS Code) │ (MCP protocol, JSON-RPC over stdio/HTTP) ▼ Uedu MCP Server ← subject of this document │ (HTTPS, Bearer token) ▼ Uedu Public API v1 (/api/v1/*) │ ▼ MySQL database (querying low-sensitivity fields only)
Actual data queries, permission checks, and rate limiting all happen at the API layer. The MCP server is only responsible for:
This layered design ensures that changes to MCP do not affect the core API, and vice versa. If the MCP protocol is revised in future, only the wrapper needs to be rewritten.
Uedu offers two MCP transport implementations, located in the mcp_server/ directory:
| Orientation | stdio(uedu_mcp_server.py) | HTTP(uedu_mcp_http.py) |
|---|---|---|
| Transmission | stdin / stdout JSON-RPC | streamable-http(Starlette / ASGI) |
| Deploy | User’s local machine (requires Python + mcp[cli], httpx) | Runs permanently on the Uedu server, reverse-proxied to https://uedu.tw/mcp |
| Client settings | A full Python path must be specified | Fill in URL + Bearer token only |
| API key source | Environment variable UEDU_API_KEY | HTTP Authorization header, retrieved per request |
| Multi-user concurrency | Each client runs its own copy | Single server supports concurrent multiple keys |
| Applicable audience | Offline, private deployment, development | For general researchers, do not want to install dependencies |
There are two stdio versions: mcp_server/uedu_mcp_server.py (development version) and static/mcp/uedu_mcp_server.py (for users to download with curl). Whenever a tool is added or modified, all three places, including the HTTP version, must be kept in sync.
Currently provides 6 tools, all aimed at public data:
| Tool | Corresponding to Endpoint | Purpose |
|---|---|---|
list_universities | GET /api/v1/universities | List the universities included (filterable by region) |
search_courses | GET /api/v1/courses | Search public courses (keywords, semester, school, pagination) |
get_course | GET /api/v1/courses/<id> | Retrieve single public course details |
get_course_stats | GET /api/v1/course_stats | Course statistics (by semester / academic year / institution grouping) |
list_papers | GET /api/v1/papers | Academic papers published by the Uedu team |
list_conferences | GET /api/v1/conferences | Academic conferences attended by the Uedu team |
All tools follow the conventions below:
@mcp.tool() decorator in FastMCPstr (JSON serialisation, ensure_ascii=False to preserve Chinese)Context parameter, used to retrieve the per-request Bearer tokenuedu_<48 hex> (53 characters in total)Authorization: Bearer <key> → compute SHA-256 → check api_v1_keys.key_hash → verify is_active=1Each API request updates two buckets at the same time (INSERT... ON DUPLICATE KEY UPDATE, atomic):
bucket_minute = DATETIME.replace(second=0, microsecond=0)bucket_day = DATE| Layer | Default maximum | can override |
|---|---|---|
| Per minute | 60 times | api_v1_keys.rate_limit_per_minute (NULL = use default) |
| Daily | 10,000 times | api_v1_keys.rate_limit_per_day (NULL = use default) |
Administrators can raise or lower the limit for individual keys via /api/developers/admin/keys/<id>, for example allowing a trusted research institution key to be relaxed to 300/min.
Each API call is written to api_v1_request_log (including endpoint, HTTP status code, IP, User-Agent), retained for 30 days, for abuse detection and debugging.
The MCP Server is Uedu's external "gateway", and the data-boundary rules must be strict:
| Type | Field example |
|---|---|
| University metadata | code、name_zh、name_en、region |
Public Course (is_public=1) | class_name、instructor、semester、department、teaching_goal |
| Course statistics | Aggregated course_count and instructor_count by semester / academic year / school |
| Paper metadata | Title, author, conference, year, DOI, category |
| Seminar metadata | Name, location, date, number of papers presented |
is_public=0)The data boundary does not rely on “judgement at the time of request”; instead, sensitive fields are not retrieved at the SQL layer. The SELECT statement for each Public API endpoint is designed as a whitelist, and sensitive fields never enter the API response path. The WHERE is_public = 1 AND deleted = 0 clause is hard-coded into each course endpoint to prevent enumeration attacks.
When adding a tool, the following three places must be updated at the same time:
mcp_server/uedu_mcp_server.py (stdio development version)static/mcp/uedu_mcp_server.py (downloadable version for users)mcp_server/uedu_mcp_http.py (HTTP version)Update app_api_v1.py as needed (to add new endpoints) and templates/developers/index.html (user documentation).
At present, Flask-Limiter uses in-memory storage by default. In a multi-worker deployment, Redis must be used, otherwise each worker's count is independent and the actual limit will become N times larger.
The HTTP MCP server must be run as a separate process bound to 127.0.0.1:5050 (managed by supervisord), and nginx must be configured with a location /mcp/ reverse-proxy rule. It is currently marked as experimental.
In addition to tools, the MCP specification also defines Resources and Prompts. Uedu plans to add these gradually:
Expose structured resources via URI, so users can add them on the LLM client side via "Add Context → MCP Resources":
uedu://courses/<id> → course outlineuedu://papers/<id> → paper metadata and abstractuedu://universities/<code>/courses → all public courses at a universityPredefined interactive prompt template, triggered when the user types /uedu.xxx in the LLM client:
/uedu.find_advisor (corresponds to the advisor_match module)/uedu.summarize_course (corresponds to the course module)/uedu.related_papers (search the Uedu paper collection by interest keywords)Plan to submit to the GitHub MCP Registry, allowing users to install with one click in VS Code via @mcp uedu.
The Uedu MCP Server itself is a research subject. Possible topics to explore:
When citing this system, please cite: "Uedu MCP Server: an educational-data MCP implementation with dual transport (stdio + streamable-http) (https://uedu.tw/developers)". If using API v1 to obtain data, please also state the endpoint used, the query period and the data scope. Before publication, be sure to confirm that all data used belong to the "public" category; private fields must not appear in the research materials.