Skip to main content
Glama

waterloo-learn-mcp

MCP server exposing your Waterloo LEARN courses to AI apps. LEARN sits behind WatIAM + Duo, so auth is split out: you log in once in a real browser, the session is saved to auth.json, and the server reuses those cookies over plain HTTP until they expire. (A headless browser is only launched for rare scrape fallbacks, so npx playwright install chromium is still part of setup.)

auth.json, oauth.json, and .env.local hold secrets. They are gitignored — treat them like passwords.

Install

npm install
npx playwright install chromium
npm run build
npm run login          # browser opens; sign in + approve Duo. Saves auth.json

Optional: npm run login can autofill your WatIAM username/password before waiting for Duo. Add both values to .env.local or export them in your shell:

WATIAM_USERNAME=your-watiam-user
WATIAM_PASSWORD=your-watiam-password

Related MCP server: Horizon

Tools

Tool

Args

Returns

list_courses

Course names + ou IDs

get_announcements

courseId

Announcements (title, body, date, attachments)

get_content

courseId, query?

Content modules/topics with URLs; query returns only matching hierarchy paths

get_topic_file

courseId, topicId, pages?

Adaptive whole-deck contact sheets, or detailed slide images when pages is supplied

get_grades

courseId

Grade items (grade, points, weight, feedback)

get_assignments

courseId

Assignments with due dates, instructions, your submission status + files, released feedback

get_upcoming

courseId, daysAhead?

Due dates / events (default 30 days)

get_course_outline

courseId

Official course outline/syllabus text from the local cache, refreshing from Outline.uwaterloo.ca when missing

Connect to Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json, then restart the app:

{
  "mcpServers": {
    "waterloo-learn": {
      "command": "node",
      "args": ["/absolute/path/to/waterloo-learn-mcp/dist/index.js"]
    }
  }
}

Local, no tunnel, no exposed session. Details: skills/connect-claude-desktop/SKILL.md.

Connect to a web chat with ngrok

ChatGPT and Claude.ai are cloud-hosted, so they need a public HTTPS URL for this MCP server. The default path is ngrok: the MCP server runs on your laptop, and ngrok publishes it at a stable HTTPS dev domain.

First, install and sign in to ngrok:

brew install ngrok/ngrok/ngrok
ngrok config add-authtoken <your-ngrok-authtoken>

Get your free dev domain from the ngrok dashboard, then run the one-time setup:

npm run setup:ngrok

When prompted, paste the domain only, for example example.ngrok-free.app. The script saves it to .env.local, starts the local HTTP server, starts ngrok, and prints your connector URL:

https://<your-ngrok-domain>/mcp

Use OAuth. Leave OAuth Client ID and Client Secret blank. When the authorization page opens, paste the connection code printed by the setup script (LEARN_MCP_TOKEN from .env.local).

  • ChatGPT → Settings → Connectors → Developer mode → Add custom connector → Server URL.

  • Claude.ai → Settings → Connectors → "+" → name + URL.

OAuth clients and tokens are saved in oauth.json so connectors keep working after server restarts. Treat LEARN_MCP_TOKEN like a password. The legacy URL https://<your-ngrok-domain>/mcp/<secret> still works for clients that cannot use OAuth.

After a reboot with ngrok

Your ngrok domain, MCP token, OAuth clients, and LEARN session all persist. After restarting your computer, run:

npm run start:ngrok

Leave that terminal running while you want ChatGPT or Claude.ai to reach LEARN.

If the LEARN session expired, start:ngrok exits before starting the tunnel. You can also check it directly:

npm run check:auth

To check the tunnel from another terminal:

curl -s -H 'ngrok-skip-browser-warning: true' https://<your-ngrok-domain>/health
curl -s -H 'ngrok-skip-browser-warning: true' https://<your-ngrok-domain>/.well-known/oauth-authorization-server

Expected:

  • /health -> ok

  • OAuth metadata -> JSON with a registration_endpoint

ngrok may show a browser warning during OAuth. Click through once. Command-line checks can skip it with the ngrok-skip-browser-warning header shown above.

Tailscale alternative

Tailscale Funnel also works if you prefer it. It has a nice reboot story because Funnel persists its tunnel config; only the local HTTP server needs to be running.

npm run setup:tailscale
npm run start:http
npm run autostart:http  # optional: start HTTP server on login
npm run stop:http       # stop HTTP autostart and free port 8787

Use this connector URL:

https://<device>.ts.net/mcp

After a reboot with Tailscale:

  • Ran autostart:http -> nothing to do; it restarts itself on login.

  • Didn't -> npm run start:http to bring the server back.

If Claude/ChatGPT cannot fetch OAuth config or cannot connect to <device>.ts.net:443, reset Tailscale Serve/Funnel:

tailscale funnel reset
tailscale serve reset
tailscale funnel --bg 8787

Notes

  • list_courses uses the enrollments API, falling back to homepage scraping. Official titles from Outline.uwaterloo.ca are cached in cache/outlines/viewer-outlines.json and reused when the outline session is expired. Other tools call D2L's REST API through the authenticated session.

  • get_course_outline reads cache/outlines/ first. Cached outlines are checked against the published revision date and automatically refetched when the instructor publishes a new revision. If a course is not cached, it checks Outline.uwaterloo.ca's enrolled-course viewer, then falls back to outline links posted in LEARN content. If neither exists, look for an uploaded outline/syllabus PDF in get_content.

  • get_content(courseId, query="Week 13 Meeting 1") searches module paths and topic titles before returning data, avoiding a full-semester tool response when only one item is needed. Omit query for the complete hierarchy.

  • get_topic_file returns native MCP images so the model can read diagrams, not just text. Without pages, decks of up to 4 slides return individual PNGs; larger decks return numbered JPEG contact sheets with up to 12 slides each (75-slide overview cap). Pass pages="4", pages="2-6", or pages="2,4,7-9" for detailed 800px PNGs (30-slide cap per call).

  • Downloaded PDFs and converted PowerPoints are cached in-process for 15 minutes, up to 100 MB total, so overview-to-detail follow-ups avoid another LEARN download or LibreOffice conversion. PDFs need nothing extra; PowerPoint topics need LibreOffice (brew install --cask libreoffice). Works in Claude (Desktop + Claude.ai) and ChatGPT.

  • "No valid LEARN session" (or tools failing after weeks) = session expired → npm run login again. Independent of reboots.

  • Set LEARN_SKIP_OUTLINE_REFRESH=1 in .env.local to keep startup-triggered logins from opening Outline.uwaterloo.ca or rebuilding the outline cache. Existing cached outlines are still used; live outline lookups can still refresh individual entries when requested.

  • Override with env vars: LEARN_BASE_URL, LEARN_AUTH_FILE, LEARN_OUTLINE_CACHE_DIR, LEARN_SKIP_OUTLINE_REFRESH, LEARN_TOPIC_FILE_CACHE_TTL_MS, LEARN_TOPIC_FILE_CACHE_MAX_BYTES, PORT, LEARN_MCP_TOKEN, WATIAM_USERNAME, WATIAM_PASSWORD, WATIAM_LOGIN_DOMAIN.


Disclaimer

This is an unofficial, independent tool built for personal academic use. It is not affiliated with, endorsed by, or supported by the University of Waterloo, D2L, or any related entity. "Waterloo LEARN" and related marks belong to their respective owners.

The server acts on your behalf using your own credentials to access only your own course data — it accesses nothing you couldn't already see by logging into LEARN yourself. You are responsible for using it in accordance with the University of Waterloo's acceptable-use policies and LEARN's terms of service. Don't use it to access data that isn't yours, and don't share course content in ways that violate copyright or your instructors' wishes.

LEARN's internal APIs and page structure can change without notice, which may break this tool at any time. It is provided as-is, without warranty of any kind. AI models can also misread or hallucinate content — always verify grades, due dates, and other important information against LEARN directly. Use at your own risk.

Available Tools

5 tools
get_announcementsGet AnnouncementsA

Get announcements/news posted by instructors for a course. Returns title, body, posted date, and attachments. Use to answer: "Any new announcements?", "What did the professor post?"

ParametersJSON Schema
NameRequiredDescriptionDefault
courseIdYesThe course org unit (ou) ID from list_courses, e.g. 123456

TDQS

A3.7/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, and description does not disclose behavioral traits such as read-only nature, required permissions, rate limits, or potential side effects. Only describes return structure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences efficiently convey purpose, return data, and example usage. No redundant information, front-loaded with key details.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given low complexity (single param, no output schema), the description covers purpose and param but lacks behavioral context. Missing information about authentication, scope (e.g., only current user?), and return limitations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with a clear description for the single parameter (courseId). Description adds no extra meaning beyond the schema, so baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states verb 'Get' and resource 'announcements/news posted by instructors for a course'. Specifies returned fields (title, body, posted date, attachments), distinguishing it from sibling tools like get_grades or get_content.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides example queries ('Any new announcements?', 'What did the professor post?') that clarify when to use. However, does not explicitly mention when not to use or contrast with alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_contentGet Course ContentA

List the content modules and materials (lectures, slides, files, links) for a course as a nested table of contents. Use to answer: "What materials are in week 3?", "Where are the lecture slides?"

ParametersJSON Schema
NameRequiredDescriptionDefault
courseIdYesThe course org unit (ou) ID from list_courses, e.g. 123456

TDQS

A4.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden. It discloses the tool is a read operation that returns a nested TOC. However, it lacks details about any access requirements, pagination, or whether only published content is returned. This is adequate for a simple list tool but could be more transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise: two sentences with no fluff. The first sentence states the core purpose and output format, and the second provides example use cases. Every word adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simplicity (single parameter, no output schema, no nested objects, and clear sibling tools), the description is complete. It explains the output (nested table of contents) and gives concrete examples. No critical information is missing for an agent to correctly invoke this tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already provides a high-coverage description for the only parameter (courseId as 'The course org unit (ou) ID from list_courses'). The description adds context by implying the parameter identifies the course, but does not add significant new meaning beyond the schema. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'List the content modules and materials... for a course as a nested table of contents.' It provides specific verb ('list') and resource ('content modules and materials'), and the title 'Get Course Content' reinforces this. The examples delineate it from siblings like get_announcements or get_grades.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description includes example queries ('What materials are in week 3?', 'Where are the lecture slides?') that help the agent understand when to use this tool. It implicitly distinguishes from sibling tools by focusing on content structure. However, it does not explicitly state when not to use it or list alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_gradesGet GradesA

Get your grades for a course: each grade item with displayed grade, points, weight, and feedback. Use to answer: "What are my grades?", "How did I do on the midterm?"

ParametersJSON Schema
NameRequiredDescriptionDefault
courseIdYesThe course org unit (ou) ID from list_courses, e.g. 123456

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Describes return content (grade items with grade, points, weight, feedback) but does not mention error handling, authentication, or rate limits. No annotations provided to supplement.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no redundancy, front-loading the main action and purpose. Every sentence adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Adequately explains tool behavior for a simple read operation with one parameter. Although no output schema, the description lists return fields. Could mention that grades are for the current user, but 'your grades' implies that.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema already fully describes the single parameter (courseId), including its source. Description does not add further semantic value beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly specifies verb 'Get', resource 'grades', and scope 'for a course'. Differentiates from sibling tools like get_announcements or list_courses.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides concrete query examples ('What are my grades?', 'How did I do on the midterm?') that indicate when to use the tool. However, lacks explicit when-not-to-use or alternative tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_upcomingGet Upcoming Due DatesA

Get upcoming calendar events and due dates (assignments, quizzes, exams) for a course. Use to answer: "What is due this week?", "When is the next deadline?"

ParametersJSON Schema
NameRequiredDescriptionDefault
courseIdYesThe course org unit (ou) ID from list_courses, e.g. 123456
daysAheadNoHow many days ahead to look (default 30)

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so the description must fully disclose behavior. It specifies the resource (upcoming calendar events and due dates) and implies read-only access, but does not mention edge cases (e.g., if no events exist), output ordering, or any side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences achieve multiple goals: stating purpose, giving use case examples, and being front-loaded. No unnecessary words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description is adequate for a simple list tool, but lacks details about return format, sorting, and whether it includes all event types. Given no output schema, more specificity would improve completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with descriptions for both parameters. The tool description adds no additional meaning beyond the schema (e.g., courseId is already explained as 'ou ID from list_courses'). Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it gets upcoming calendar events and due dates for a course, with specific example questions. This distinguishes it from siblings like get_announcements (announcements) and get_grades (grades).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides concrete example queries ('What is due this week?', 'When is the next deadline?') that indicate when to use the tool. However, it does not explicitly exclude scenarios or contrast with alternatives, which would be helpful.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_coursesList CoursesA

List the courses you are enrolled in on Waterloo LEARN. Returns course names and their ou (org unit) IDs, which the other tools take as courseId. Use to answer: "What courses am I taking?"

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, leaving the description as the sole source. It states the tool returns course names and IDs, which is basic behavioral info, but does not disclose authentication, rate limits, or any other side effects. The behavior is straightforward but not exhaustive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no redundant words. Essential information is front-loaded: the action, the platform, the output, and its usage. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema, the description sufficiently explains the return values and their purpose. Sibling tools (e.g., get_announcements) require a courseId, so this tool's role as a prerequisite is clear. The description is complete for a parameterless list tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has no parameters, so schema coverage is 100%. Baseline for 0 params is 4. The description adds value by explaining the output format and how it integrates with other tools, exceeding the baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states it lists courses the user is enrolled in on Waterloo LEARN, specifies the output (course names and ou IDs), explains their use by other tools, and gives a canonical query. This clearly distinguishes it from sibling tools that operate on individual courses.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description includes a direct use case ('What courses am I taking?') and explains that the returned IDs are used as courseId by other tools, implying this tool should be called first. However, it does not explicitly mention when not to use it or alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 5 tool updatesv0.1.0
    • First observedget_announcements
    • First observedget_content
    • First observedget_grades
    • First observedget_upcoming
    • First observedlist_courses

TDQS

A4.2/5.0
Disambiguation5/5

Each tool targets a distinct aspect of the LEARN experience (announcements, content, grades, upcoming events, courses), with no overlaps in purpose.

Naming Consistency5/5

All tool names follow the consistent pattern get_<resource> in snake_case, making them predictable and easy to remember.

Tool Count5/5

5 tools is an ideal size for a student-focused LMS server, covering all essential read operations without unnecessary bloat.

Completeness5/5

The toolset provides comprehensive coverage of a student's common needs: viewing courses, grades, materials, announcements, and upcoming deadlines. No obvious gaps.

Maintenance

ActivityMaintained
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

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/petersenmatthew/waterloo-learn-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server