An MCP (Model Context Protocol) server that connects LLMs to WeBWorK online homework systems. Read your assignments, check due dates, track progress, and download hardcopies — all from your AI assistant.
webwork-mcp logs into your WeBWorK courses and exposes a read-only set of tools that any MCP-compatible client (Claude Desktop, Cursor, Claude Code, Poke, etc.) can use to:
- List your enrolled classes and homework sets
- Check due dates and upcoming deadlines
- Read individual problems with full LaTeX math rendering
- Track your progress and grades
- Download PDF hardcopies of assignments
Read-only by design. This server cannot submit or preview answers on your behalf.
| Tool | Description |
|---|---|
get_classes |
List all enrolled classes with username and URL |
get_course_info |
Full overview of one class — open/closed sets, progress, due dates |
get_all_courses_info |
Same as above but for every class at once |
get_dashboard |
Lightweight cross-class view of open sets and deadlines |
get_all_sets |
Every homework set in a class |
get_open_sets |
Only currently-open sets |
get_due_dates |
Due dates for all sets in a class |
get_upcoming_deadlines |
Due dates for open sets only |
get_set_info |
Problem list with attempts, scores, and point values |
get_set_progress |
Quick done/todo summary for a set |
get_problem |
Full problem content with inline LaTeX |
get_grades |
Per-set scores and percentages |
download_hardcopy |
Generate and save a PDF of a homework set |
- Python 3.13+
- uv
git clone https://github.com/yourusername/webwork-mcp.git
cd webwork-mcp
uv syncCreate a .env file in the project root:
url=https://webwork.example.edu/webwork2
classes=<class0-from-url>,<class1-from-url>
username0=your.username
password0=your_password
username1=your.username2
password1=your_password2
url— base WeBWorK URL (everything before the class name)classes— comma-separated list of class names as they appear in the URLusername0/password0— credentials for the first classusername1/password1— credentials for the second class, and so on
⚠️ Keep your.envfile private. It is already in.gitignore— never commit it.
The easiest way to get the server running and connected to an LLM is with Poke. Start the HTTP server, then tunnel it:
# Terminal 1 — start the MCP server
uv run fastmcp run server.py:mcp --transport http --port 3000
# Terminal 2 — expose it via Poke tunnel
npx poke@latest tunnel http://localhost:3000/mcp -n "WeBWorK MCP"The tunnel stays active until you press Ctrl+C. Once it's up, the server is available as an integration in your Poke account.
To share it with others (generates a recipe link + QR code):
npx poke@latest tunnel http://localhost:3000/mcp -n "WeBWorK MCP" --shareFor local clients that support stdio (Claude Desktop, Cursor, Claude Code):
uv run server.pyOr explicitly with the FastMCP CLI:
uv run fastmcp run server.py:mcp --transport stdiouv run fastmcp run server.py:mcp --transport http --port 3000The MCP endpoint will be at http://localhost:3000/mcp.
Add to your Claude Desktop MCP config (claude_desktop_config.json):
{
"mcpServers": {
"webwork": {
"command": "uv",
"args": ["run", "--directory", "/path/to/webwork-mcp", "server.py"]
}
}
}Add to .cursor/mcp.json in your project:
{
"mcpServers": {
"webwork": {
"command": "uv",
"args": ["run", "--directory", "/path/to/webwork-mcp", "server.py"]
}
}
}Verify everything works without an MCP client:
uv run main.pyThis logs into all configured classes, lists sets, shows progress for open assignments, and fetches a sample problem with LaTeX.
The image runs the MCP server and a Poke tunnel in one container, so the server is exposed via Poke without a second process.
docker build -t webwork-mcp ..env— Use--env-file .envor bind the file so the app can read WeBWorK config:-v "$(pwd)/.env:/app/.env"- Poke credentials — Poke stores credentials in
~/.config/poke/credentials.json(or$XDG_CONFIG_HOME/poke/credentials.json). Mount the host config dir so the container can reuse your login and persist new ones:-v "$HOME/.config/poke:/root/.config/poke"
docker run --rm \
--env-file .env \
-v "$HOME/.config/poke:/root/.config/poke" \
-p 3000:3000 \
webwork-mcpThe container starts the MCP server, then runs bunx poke@latest tunnel http://localhost:3000/mcp -n "Local Dev MCP". The tunnel stays in the foreground; when it exits, the container exits.
Set POKE_SHARE=1 to create a shareable connection and print a recipe link and QR code in the terminal:
docker run --rm \
--env-file .env \
-v "$HOME/.config/poke:/root/.config/poke" \
-p 3000:3000 \
-e POKE_SHARE=1 \
webwork-mcp| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
MCP HTTP server port |
POKE_NAME |
Local Dev MCP |
Name shown in Poke Kitchen |
POKE_SHARE |
(unset) | Set to 1 or true to use --share |
docker run --rm --env-file .env -v "$HOME/.config/poke:/root/.config/poke" -e PORT=8080 -p 8080:8080 webwork-mcpdocker compose up --buildUses docker-compose.yml: builds the image, loads .env, mounts ~/.config/poke for Poke credentials, and publishes port 3000. For a shareable tunnel, run with POKE_SHARE=1 docker compose up --build.
To run the HTTP server only and tunnel from the host (or another container):
docker run --rm --env-file .env -p 3000:3000 --entrypoint "" webwork-mcp \
uv run fastmcp run server.py:mcp --transport http --port 3000Then on the host: npx poke@latest tunnel http://localhost:3000/mcp -n "WeBWorK MCP".
webwork-mcp/
├── server.py # FastMCP server — exposes tools to LLM clients
├── webwork.py # WeBWorK client library — scraping and parsing
├── main.py # CLI test script
├── Dockerfile
├── docker-compose.yml
├── .dockerignore
├── pyproject.toml
├── uv.lock
└── .env # Your credentials (not committed)
LLM Client (Claude, Cursor, Poke, etc.)
│
│ MCP protocol (stdio or HTTP)
│
▼
server.py ─── FastMCP tools (13 read-only tools)
│
│ Python function calls
│
▼
webwork.py ─── WeBWorKManager / WeBWorKClient
│
│ HTTP requests + HTML parsing (requests + BeautifulSoup)
│
▼
WeBWorK server (webwork.example.edu)
- FastMCP — MCP server framework
- requests — HTTP client
- BeautifulSoup4 + lxml — HTML parsing
- python-dotenv —
.envfile loading
MIT