scottylabs-mcp
Click on "Deploy 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., "@scottylabs-mcp Search CMU courses for machine learning"
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.
scottylabs-mcp
A Model Context Protocol (MCP) server that wraps the
ScottyLabs CMU Courses API
(course-tools.apis.scottylabs.org). It lets a Claude session search the
CMU course catalog and pull full course details from inside a chat.
Tools
Tool | Description | Auth |
| Keyword/department search over the catalog. Paginated. | none |
| Full details for a single course (description, prereqs, schedules, etc.). | none |
| Just the schedules for a course — smaller payload than | none |
| Schedules taught by a given instructor. | none |
| Prereqs / postreqs / AND-of-ORs prereq relations for a course. | none |
| Gen-ed-eligible courses for a school (SCS, CIT, MCS, Dietrich). | none |
| Discover exact instructor name strings for the by-instructor tools. | none |
| FCE summary for a course (aggregates + 5 recent rows; | gated |
| FCE summary for an instructor across courses (same shape as above). | gated |
Wraps the public read endpoints of course-tools.apis.scottylabs.org
(the backend behind cmucourses.com and courses.scottylabs.org).
Related MCP server: Brown Courses MCP Server
Setup
Prerequisites
uv— provides theuvxrunner used below. Install once and you're set.
1. Wire it into Claude Desktop
Edit claude_desktop_config.json:
Windows:
%APPDATA%\Claude\claude_desktop_config.jsonmacOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonLinux:
~/.config/Claude/claude_desktop_config.json
Create the file if it doesn't exist. Add the scottylabs-cmu-courses entry
under mcpServers (merge with existing entries if present):
{
"mcpServers": {
"scottylabs-cmu-courses": {
"command": "uvx",
"args": ["scottylabs-mcp"]
}
}
}uvx fetches scottylabs-mcp from PyPI and runs it in an ephemeral
environment — no checkout, no manual install. Subsequent runs hit the uv
cache.
2. Restart Claude Desktop
Quit fully (system tray → Quit, not just close window) and reopen. In a
new chat, click the tools icon — you should see all 9
scottylabs-cmu-courses tools listed.
3. Try it in a chat
Public-endpoint examples (no auth required):
"Search CMU courses for machine learning."
"Show me the prereqs for 15-213."
"What is Iliano Cervesato teaching this semester?"
"List SCS gen-eds tagged Science."
4. (Optional) Enable FCE tools
get_course_fces and get_instructor_fces need a Clerk session JWT — see
Auth (FCE tools only) below.
Claude Code instead of Desktop?
claude mcp add scottylabs-cmu-courses --scope user -- uvx scottylabs-mcpOr paste the same mcpServers block into ~/.claude.json under your
project entry.
Local development
If you're hacking on the server, point Claude at your checkout instead:
{
"mcpServers": {
"scottylabs-cmu-courses": {
"command": "uv",
"args": [
"run",
"--directory",
"/absolute/path/to/scottylabs-mcp",
"scottylabs-mcp"
]
}
}
}Forward slashes in the path are JSON-safe on Windows. Restart Claude Desktop to pick up code changes.
Troubleshooting
Symptom | Cause / fix |
Tools don't appear after restart | Check Claude Desktop's MCP log: |
| Claude Desktop's GUI may not see your shell |
FCE tool 401s after running the auth helper | Clerk sessions expire (~7 days). Re-run |
Want to verify auth state |
|
Need to reset the saved token |
|
Anything else broken |
|
Configuration
Env var | Default | Notes |
|
| Override for local backend dev. |
| (unset) | Static Clerk session JWT for FCE tools (advanced; bypasses cookie refresh). See Auth section. |
Auth (FCE tools only)
get_course_fces and get_instructor_fces POST to /fces, which runs through
Clerk's isUser middleware. The production backend has auth enabled (verified
via the smoke harness — calls without a token come back 401), so the MCP
server needs to mint Clerk session JWTs.
Clerk session JWTs only live ~5 minutes, so the MCP server doesn't store a
JWT directly. Instead, it stores the long-lived __client cookie (~7-day TTL)
and exchanges it for a fresh JWT on each FCE call via Clerk's Frontend API.
Tokens are cached in-memory until shortly before they expire.
Option 1 (recommended): the helper
uv run --directory scottylabs-mcp scottylabs-mcp-authThe helper:
Opens
https://www.cmucourses.comin your browser.Walks you through copying the
__clientcookie from DevTools. After sign-in you'll be redirected towww.courses.scottylabs.org— that's where the cookie lives (Application → Cookies →https://www.courses.scottylabs.org→__client→ copy the Value). If it isn't there, also checkhttps://clerk.scottylabs.org.Saves the cookie to a per-user config file (
%APPDATA%\scottylabs-mcp\tokenon Windows,~/.config/scottylabs-mcp/tokenelsewhere;0o600on Unix).
The MCP server picks it up automatically and refreshes JWTs as needed. Manage it with:
scottylabs-mcp-auth --show # check where it's stored / whether it's set
scottylabs-mcp-auth --remove # delete the saved cookieRe-run the helper when:
FCE calls start returning auth errors (cookie expired, ~7 days).
You sign out of cmucourses.com (cookie revoked).
Option 2: env var (advanced)
Set SCOTTYLABS_AUTH_TOKEN to a session JWT directly. The MCP server uses
this value verbatim — no refresh — so you'll need to keep it under 5 minutes
old. Mostly useful for one-off testing. This env var wins over the saved
cookie when both are set.
What happens without auth
The first FCE call returns a ScottyLabsError whose message tells the user
to run scottylabs-mcp-auth. No silent failure.
The other endpoints (search, get-course, schedules, requisites, geneds, instructors) are public and need no auth.
Smoke test
scripts/smoke.py calls the tool functions directly against the live API,
bypassing MCP transport. Useful for sanity checks during development.
uv run --directory scottylabs-mcp python scripts/smoke.pyExpects Summary: 13/13 passed.
Layout
.
├── LICENSE
├── pyproject.toml
├── README.md
├── scripts/
│ └── smoke.py # live-API harness, bypasses MCP transport
├── tests/
│ └── test_summarizer.py # offline unit tests, run by CI
└── src/
└── scottylabs_mcp/
├── __init__.py
├── __main__.py # `python -m scottylabs_mcp`
├── auth_helper.py # `scottylabs-mcp-auth` console script
├── clerk_auth.py # Clerk Frontend API: cookie -> fresh JWT, with cache
├── client.py # shared httpx.AsyncClient + error mapping
├── models.py # pydantic types mirroring the upstream schema
├── server.py # FastMCP app, tool registrations, entry point
└── tools.py # tool implementations (importable for tests)Credits
Huge thanks to ScottyLabs — the student organization at Carnegie Mellon University that builds and maintains the Course Tool (cmucourses.com, courses.scottylabs.org) and its public backend API. This MCP server is just a thin Python wrapper around their work; all the data, scrapers, and infrastructure that make it useful are theirs.
Upstream repo: github.com/ScottyLabs/cmucourses. Consider contributing to or supporting them directly.
Available Tools
9 toolsget_courseA
Fetch full details for a single CMU course, including current schedules.
Use this when you already have the exact course ID. Accepts "15-122" or
"15122". Returns description, units, prereqs/coreqs, cross-listings,
and upcoming schedule with lectures/sections.
Args:
course_id: CMU course ID, e.g. "15-122" or "21-241".
Returns:
Course object with name, department, desc, units, prereqs,
coreqs, crosslisted, schedules, etc.
Inputs: course_id (required). Returns: courseID, name, department, desc, +8 more
get_course_fcesA
Fetch Faculty Course Evaluations (FCE) ratings for a course.
Use this when the user asks about course difficulty, hours per week,
or instructor ratings. Returns a compact summary: aggregates over all
semesters plus the 5 most recent entries. Set include_all=True only
when the user asks for the full history.
Auth: requires the env var SCOTTYLABS_AUTH_TOKEN to be set to a valid
Clerk JWT. If the upstream backend has auth disabled, the empty token
will work too.
Args:
course_id: CMU course ID, e.g. "15-122".
include_all: Default False — entries holds the 5 most recent
rows and truncated flags whether anything was cut. Pass
True to populate entries with every row. Aggregates
(avg_hrs_per_week, avg_rating, years_covered) always
reflect the full dataset.
Returns:
FCESummary with entry_count, years_covered,
avg_hrs_per_week, avg_rating, entries, truncated.
Inputs: course_id (required), include_all. Returns: Aggregate view of FCE entries for a course or instructor. The default response…
get_course_schedulesA
Fetch lecture/section schedules for a single course.
Prefer this over get_course when you only need meeting times — the
response is much smaller. Returns one schedule per offered semester
(typically several across recent years).
Args:
course_id: CMU course ID, e.g. "15-122".
Returns: List of Schedule objects.
Inputs: course_id (required). Returns: result
get_genedsA
List gen-ed-eligible courses for a CMU school.
Use this when the user wants courses satisfying a gen-ed requirement for their college.
Args:
school: Exactly one of "SCS" (School of Computer Science),
"CIT" (engineering), "MCS" (sciences), or "Dietrich"
(humanities and social sciences).
Returns:
List of Gened objects, each with course info, gen-ed tags, and
a startsCounting/stopsCounting window.
Inputs: school (required). Returns: result
get_instructor_fcesA
Fetch Faculty Course Evaluations (FCE) ratings for an instructor.
Use this for a professor's teaching record across courses, or to compare instructors. Returns a compact summary by default; aggregates span every semester they've taught.
Auth: requires SCOTTYLABS_AUTH_TOKEN (see get_course_fces).
Args:
instructor: Instructor name, exact-match (use search_instructors).
include_all: See get_course_fces. Default False keeps the
response tight.
Returns:
FCESummary (see get_course_fces).
Inputs: instructor (required), include_all. Returns: Aggregate view of FCE entries for a course or instructor. The default response…
get_instructor_schedulesA
Fetch all schedules taught by the given instructor.
Use this for "what is X teaching?" or to find classes by professor.
Pass the instructor name exactly as it appears in the course data —
use search_instructors first to discover the canonical spelling.
Args:
instructor: Instructor name, exact-match. Example: "Iliano Cervesato".
Returns: List of Schedule objects across all courses and semesters.
Inputs: instructor (required). Returns: result
get_requisitesA
Fetch the prerequisite / postrequisite graph for a course.
Use this for "what do I need before X?" or "what unlocks after X?" questions. Returns:
prereqs: required courses (flat list).prereqRelations: AND-of-ORs decoding (outer AND, inner OR).postreqs: courses that list this one as a prereq.
Args:
course_id: CMU course ID, e.g. "15-213".
Returns:
Object with prereqs, prereqRelations, postreqs.
Inputs: course_id (required). Returns: Prereq/postreq graph for a single course. `prereqRelations` is an AND-of-ORs de…
search_coursesA
Keyword-search the CMU course catalog.
Use this when the user asks about a topic, department, or partial course name and you don't already have an exact course ID. The query is matched against name, department, description, and prereq string.
Args: query: Free-text search. Examples: "machine learning", "discrete math", "Computer Science", "15-122". Department codes work too. page: 1-indexed page number. The backend caps page size at 10.
Returns:
Object with totalDocs, totalPages, page, and docs — a list of
courses with courseID, name, department, desc, units,
prereqs, etc.
Inputs: query (required), page. Returns: totalDocs, totalPages, page, docs
search_instructorsA
Look up CMU instructor names from the FCE roster.
Use this to discover the exact spelling/casing of an instructor before
calling get_instructor_fces or get_instructor_schedules. Names are
exact-match — pass them verbatim downstream.
Args:
query: Optional case-insensitive substring filter (e.g. "cervesato").
limit: Max results, default 50, hard cap 200.
Returns: List of instructor name strings.
Inputs: query, limit. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 9 tools
Each tool has a distinct purpose clearly outlined in its description. Even tools that return related data (e.g., get_course and get_course_schedules) have explicit guidance on when to use each, eliminating ambiguity.
All tool names follow a consistent verb_noun snake_case pattern with 'get_' for retrieval and 'search_' for search operations. No mixing of conventions.
With 9 tools, the server is well-scoped for the domain of CMU course information. Each tool covers a specific query need without being redundant or excessive.
The tool surface covers all major aspects of course information: details, schedules, evaluations, instructors, prerequisites, gen-eds, and search. There are no obvious gaps for the intended use case.
Maintenance
Related MCP Connectors
Unofficial NTNU course data: search, timetables, grades, course info, and exam logistics.
Unofficial Wentworth (WIT) course catalog and student schedule. Needs a WIT sign-in.
Read-only access to Epivo's live course catalogue for AI agents.
Query the full BC K-12 curriculum: Big Ideas, Competencies, Content, and more.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceMCP server for querying a university course catalog. Enables searching courses, checking prerequisites, and looking up instructors via natural language.-
- FlicenseAqualityDmaintenanceEnables searching Brown University courses, getting detailed course info, and checking schedule conflicts via natural language, using the university's public course catalog API.5-
- FlicenseNot gradedqualityDmaintenanceProvides intelligent access to a university course catalog with full-text search, prerequisite tracking, instructor lookups, and course comparison prompts.-
- AlicenseAqualityBmaintenanceEnables searching and retrieving course listings, departments, and syllabi from National Chengchi University's public course API using natural language.31MIT