cvc-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., "@cvc-mcpFind online biology courses at City College of San Francisco"
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.
California Virtual Campus (CVC) MCP Server
An open-source Model Context Protocol (MCP) server for California Virtual Campus (CVC) course search. It lets Claude Desktop and other MCP clients find online classes at California community colleges and retrieve course details, prerequisites, sections, available seats, instructors, tuition, meeting times, and zero-textbook-cost status.
This server wraps the unofficial
CaliforniaVirtualCampusAPI
Python library and public data from CVC Course Search.
No API key is required.
Features
Search California Virtual Campus course IDs by college, C-ID, local course code, or title
Retrieve structured CVC course and section data for AI assistants
Find online California community college classes from Claude Desktop
Return source URLs so users can verify current information on CVC
Limit results to protect the model context window
Offer optional Selenium browser-search fallbacks for difficult queries
Run locally over stdio; no hosted service, account, or API key required
Deploy as a stateless Streamable HTTP MCP server on Vercel
Related MCP server: Canvas LMS MCP Server
MCP tools
Tool | Description |
| Search one college by C-ID, course symbol, or course name using CVC's public search endpoint |
| Get course information, sections, and available seats per section by CVC course ID |
| Experimental headless-browser course search |
| Experimental headless-browser search with full course details |
For reliable results, call search_course_ids first and pass an ID to get_course.
The two scrape_* tools require a local Chrome or Chromium installation and a compatible
ChromeDriver. They are slower and more sensitive to CVC website changes.
Requirements
Python 3.10 or newer
uv(recommended) orpipInternet access to
search.cvc.eduChrome/Chromium only when using the experimental
scrape_*tools
Install
git clone https://github.com/SanjayMarathe/cvc-mcp.git
cd cvc-mcp
uv sync --frozenRun the server locally:
uv run --frozen cvc-mcpThe process waits for MCP messages on standard input; a blank terminal is expected.
Install with pip
python -m venv .venv
source .venv/bin/activate
python -m pip install .
cvc-mcpAdd CVC MCP to Claude Desktop
Open the Claude Desktop configuration file:
Operating system | Configuration path |
macOS |
|
Windows |
|
Linux |
|
Add this entry under mcpServers, replacing both paths with absolute paths on your
computer:
{
"mcpServers": {
"cvc": {
"command": "/absolute/path/to/uv",
"args": [
"--directory",
"/absolute/path/to/cvc-mcp",
"run",
"--frozen",
"cvc-mcp"
]
}
}
}Fully quit and reopen Claude Desktop. Ask Claude to list its tools if the CVC tools do not appear immediately.
Hosted MCP and documentation
This repository includes a Vercel-compatible Python ASGI deployment:
/— browsable setup and tool documentation/docs— redirects to the documentation homepage/health— JSON health check/mcp— stateless Streamable HTTP MCP endpoint
Deploy your own copy with the Vercel CLI:
vercel
vercel --prodVercel supplies the deployment hostname automatically. The server uses it to enforce
MCP host and origin checks. If you deploy through another ASGI platform, set
CVC_MCP_HOST to its hostname without https://.
Connect a remote MCP client with:
{
"mcpServers": {
"cvc": {
"url": "https://your-project.vercel.app/mcp"
}
}
}The hosted MCP endpoint is public and read-only. No API key is required. The
scrape_* tools require Chrome and therefore are intended for local use; use
search_course_ids and get_course on Vercel.
Production deployment: cvc-mcp.vercel.app
Streamable HTTP endpoint: https://cvc-mcp.vercel.app/mcp
Example prompts for Claude
“Search CVC for online computer science courses at Pasadena City College.”
“Find the CVC course IDs for COMP 122 and show me the available sections.”
“Does this California community college course have open seats or zero textbook cost?”
“Compare the instructors, meeting times, and tuition for these CVC course IDs.”
“Find an online California community college class that matches C-ID COMP 122.”
Tool examples
Search for matching IDs:
{
"college_name": "Pasadena City College",
"course_symbol": "COMP 122",
"max_results": 10
}Retrieve course details:
{
"course_id": 12345
}Each item in the returned sections array includes an integer available_seats value:
{
"section": "78499",
"semester": "Fall 2026",
"available_seats": 8
}How it works
The server uses the official Python SDK for the Model Context Protocol and delegates CVC
lookups to CaliforniaVirtualCampusAPI 0.0.2. The upstream library reads public CVC web
pages and a public search endpoint, then this MCP server converts its Python course and
section objects into stable, model-friendly JSON.
All requests run locally from your computer. There is no API key, analytics service, or intermediate hosted backend in this project.
Limitations and data accuracy
This is an independent, unofficial project. It is not affiliated with or endorsed by California Virtual Campus, the California Community Colleges Chancellor's Office, or Anthropic.
The upstream wrapper is alpha software and depends on CVC's current HTML structure.
Course availability, seat counts, tuition, dates, transferability, and prerequisites can change. Always verify results on the returned
search.cvc.edusource URL and with the teaching college before enrolling.College-name matching is performed by the upstream package and may select a similarly named institution when given an ambiguous or misspelled name.
CVC can change or restrict its public endpoints at any time.
Development
uv sync --all-groups
uv run pytest
uv run ruff check .
uv run ruff format --check .Run the opt-in end-to-end suite against the production deployment:
CVC_MCP_RUN_E2E=1 uv run pytest tests/e2eOverride CVC_MCP_E2E_URL to test another deployment.
Run the HTTP deployment locally:
uv run uvicorn app:app --reload
# Documentation: http://127.0.0.1:8000/
# MCP endpoint: http://127.0.0.1:8000/mcpData source and attribution
Course data is provided by California Virtual Campus and is described by the upstream project as available under the Creative Commons Attribution 4.0 International license. The wrapper dependency is licensed under Apache-2.0. This MCP server's original code is available under the MIT License.
Related terms
California Virtual Campus API, CVC API, CVC Exchange, CVC Course Finder, California community college online courses, California online classes, Claude Desktop MCP server, Model Context Protocol course search, community college course availability.
Available Tools
4 toolsget_courseB
Get a CVC course and its sections, including available seats for each section.
| Name | Required | Description | Default |
|---|---|---|---|
| course_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so description carries the burden. It describes the retrieval operation but doesn't mention side effects, data freshness, or error cases. For a read tool, some behavioral context is missing, but it's not misleading.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One sentence with no waste and the key detail (sections and seat availability) front-loaded. Could add a hint about sibling tools, but compact is good.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Output schema exists and covers return structure. But without annotations or sibling differentiation, the description is sufficient for a simple lookup yet still lacks guidance about where course_id comes from and the relation to search_course_ids.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It identifies course_id as the lookup key by naming the resource, but doesn't explain how to find course IDs or what format/edge cases exist. Better than nothing, but minimal.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description uses specific verb 'Get' with resource 'CVC course', and adds 'including available seats for each section' as useful detail. It doesn't explicitly differentiate from siblings, but it is clear what it does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit when-to-use guidance or mention of alternatives. Context signals show siblings like scrape_courses, but description doesn't route. Implied usage: retrieving a single course by ID. Barely above no guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
scrape_course_idsA
Search CVC course IDs through a headless Chrome browser.
This experimental fallback is slower and requires Chrome/Chromium plus a compatible driver. Prefer search_course_ids for ordinary searches.
| Name | Required | Description | Default |
|---|---|---|---|
| c_id | No | ||
| course_name | No | ||
| max_results | No | ||
| college_name | Yes | ||
| course_symbol | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description must carry the behavioral disclosure burden. It does disclose important operational traits: slower performance, experimental status, and external browser/driver requirements. However, it does not mention failure modes if Chrome/driver is missing, whether this is read-only, or how scraping behaves under edge cases.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the core purpose, followed by the dependency warning and the preferred alternative. Every sentence earns its place with no filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description supplies strong high-level context: it identifies the fallback nature, the external dependency, and the preferred sibling. But with five parameters completely undocumented and no mention of required college_name or filter behavior, it is only partially complete for invoking the tool correctly despite the presence of an output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, and the description adds no parameter semantics at all. It never explains college_name (the required field), c_id, course_name, course_symbol, or max_results, nor how these filters combine or behave with defaults.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a clear verb-resource pair: 'Search CVC course IDs' via a headless Chrome browser. It also differentiates itself from the sibling search_course_ids by labeling itself as an experimental fallback, so an agent can distinguish the tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Prefer search_course_ids for ordinary searches' is an explicit routing instruction to a sibling. The description also gives concrete selection context: it is slower, experimental, and requires Chrome/Chromium plus a compatible driver.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
scrape_coursesA
Search CVC in a headless browser and return detailed matching courses.
This experimental operation can be slow because it loads a page for every result. It requires Chrome/Chromium and a compatible driver. Prefer search_course_ids followed by get_course when possible.
| Name | Required | Description | Default |
|---|---|---|---|
| c_id | No | ||
| course_name | No | ||
| max_results | No | ||
| college_name | Yes | ||
| course_symbol | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It discloses key behavioral traits: it is experimental, slow (loads a page per result), and requires Chrome/Chromium and a compatible driver. This is valuable context for an agent. However, it omits potential failure modes (e.g., network errors, rate limits) and the exact nature of the returned data, though the output schema covers the latter. A 4 reflects strong but not exhaustive transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences, each earning its place: purpose, caveats, and alternative. It front-loads the purpose and keeps the caveats and guidance concise. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that an output schema exists (so return values are covered), the description provides the essential context: what it does, its limitations, and when to prefer alternatives. It lacks explicit details on how parameters are used together, but the schema fills some gaps. For a tool of this complexity, it is adequately complete for an agent to decide and execute.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate by explaining parameter semantics. It does not mention any parameters. The schema itself provides titles (e.g., 'C Id', 'Course Name', 'Max Results') and defaults, which are somewhat self-explanatory, but the description offers no guidance on how parameters interact or how filtering works. This is a significant gap for a tool with 5 parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool 'Search CVC in a headless browser and return detailed matching courses.' This specifies a verb (search), a resource (CVC), and a distinct outcome (detailed matching courses). It also implicitly distinguishes from siblings by noting the preferred alternative search_course_ids + get_course, making its heavier nature apparent.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says 'Prefer search_course_ids followed by get_course when possible,' giving a clear directive for when not to use this tool. However, it does not state the conditions under which this tool should be used (e.g., when the lighter path is insufficient or unavailable), leaving some inference required. Still, the guidance on alternatives is explicit and helpful.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_course_idsA
Search CVC course IDs at a California community college.
Use a college name plus at least one filter. C-ID is the statewide Course Identification Numbering System value; course_symbol is the local code such as COMPSCI001; course_name is a title phrase. This is the preferred, lightweight search.
| Name | Required | Description | Default |
|---|---|---|---|
| c_id | No | ||
| course_name | No | ||
| max_results | No | ||
| college_name | Yes | ||
| course_symbol | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It only says it's 'lightweight' (hinting at performance) but gives no details on rate limits, authorization, side effects (though search is inherently read-only), or any edge-case behavior. It doesn't contradict anything, but it is thin on transparency beyond the lightweight hint.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with zero filler. The purpose and key usage rule are front-loaded, and the field definitions are packed efficiently. Every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given an output schema exists (so return values are covered), the description adequately addresses usage and parameter semantics. It doesn't mention limitations like pagination or rate limits, but for a lightweight search tool that's acceptable. It also hints at sibling differentiation, which is sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description is the sole source of parameter meaning. It explicitly defines c_id as 'statewide Course Identification Numbering System value', course_symbol as 'local code such as COMPSCI001', and course_name as 'title phrase'. It also implies max_results is a limit through its name. This compensates well for the missing schema descriptions, though max_results isn't explicitly clarified.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Search CVC course IDs at a California community college.' It also specifies the required input (college name plus filter) and distinguishes itself as 'the preferred, lightweight search' relative to heavier siblings like scrape_course_ids. The purpose is unmistakable even without inspecting the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit guidance: 'Use a college name plus at least one filter.' It also explains the meaning of c_id, course_symbol, and course_name, and labels the tool as lightweight, implying it should be used for quick lookups rather than scraping. It doesn't explicitly name the sibling alternatives, but the context signals list them and the description's phrasing makes the usage context clear.
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.
4 tool updates
v0.1.1- First observed
get_course - First observed
scrape_course_ids - First observed
scrape_courses - First observed
search_course_ids
TDQS
Scored across 4 tools
get_course and scrape_courses are clearly distinct, but search_course_ids and scrape_course_ids both search for CVC course IDs and differ mainly by implementation. The descriptions help by marking one as preferred and the other as experimental fallback, but the boundary is still somewhat blurred.
All tools follow a consistent snake_case verb_noun pattern: get_course, search_course_ids, scrape_course_ids, scrape_courses. The verbs vary by operation type, but the structure is uniform and predictable.
Four tools is slightly small but well-scoped for a course search server. Each tool serves a clear retrieval or search purpose, and the count does not feel excessive or artificially padded.
The server covers ID search, course detail retrieval, and a detailed search fallback. A minor gap is the lack of a direct non-scrape detailed course search, but the existing combination of search_course_ids plus get_course and scrape_courses covers the core workflow.
Maintenance
Related MCP Connectors
Read-only access to Epivo's live course catalogue for AI agents.
Search disc golf courses worldwide, find nearby courses, and retrieve course details and updates.
Search US grants + federal contracts (Grants.gov + SAM.gov) from any LLM.
AI-agent product catalog: search, lookup & purchase routing over verified merchant data.
Related MCP Servers
- FlicenseAqualityNot gradedmaintenanceEnables interaction with Canvas LMS courses and assignments directly from your LLM, allowing you to retrieve, search, and summarize course information, check due dates, and access assignment details without leaving your AI assistant.4111 npm-
- AlicenseAqualityCmaintenanceEnables AI systems to interact with Canvas Learning Management System data, allowing users to access courses, assignments, quizzes, planner items, files, and syllabi through natural language queries.227MIT
- FlicenseNot gradedqualityCmaintenanceProvides intelligent access to a university course catalog with full-text search, prerequisite tracking, instructor lookups, and course comparison prompts.-
- AlicenseAqualityAmaintenanceExposes USC's public course catalog API to AI agents, enabling natural-language queries about courses, GE requirements, schedules, and fees with optional filters for days, times, and open sections.16MIT