Skip to main content
Glama

California Virtual Campus (CVC) MCP Server

Model Context Protocol Python License: MIT

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_course_ids

Search one college by C-ID, course symbol, or course name using CVC's public search endpoint

get_course

Get course information, sections, and available seats per section by CVC course ID

scrape_course_ids

Experimental headless-browser course search

scrape_courses

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) or pip

  • Internet access to search.cvc.edu

  • Chrome/Chromium only when using the experimental scrape_* tools

Install

git clone https://github.com/SanjayMarathe/cvc-mcp.git
cd cvc-mcp
uv sync --frozen

Run the server locally:

uv run --frozen cvc-mcp

The 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-mcp

Add CVC MCP to Claude Desktop

Open the Claude Desktop configuration file:

Operating system

Configuration path

macOS

~/Library/Application Support/Claude/claude_desktop_config.json

Windows

%APPDATA%\\Claude\\claude_desktop_config.json

Linux

~/.config/Claude/claude_desktop_config.json

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 --prod

Vercel 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.edu source 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/e2e

Override 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/mcp

Data 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.

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 tools
get_courseB

Get a CVC course and its sections, including available seats for each section.

ParametersJSON Schema
NameRequiredDescriptionDefault
course_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.2/5.0
Behavior2/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
c_idNo
course_nameNo
max_resultsNo
college_nameYes
course_symbolNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.8/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters1/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
c_idNo
course_nameNo
max_resultsNo
college_nameYes
course_symbolNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters2/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
c_idNo
course_nameNo
max_resultsNo
college_nameYes
course_symbolNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

  1. 4 tool updatesv0.1.1
    • First observedget_course
    • First observedscrape_course_ids
    • First observedscrape_courses
    • First observedsearch_course_ids

TDQS

A3.7/5.0

Scored across 4 tools

Disambiguation3/5

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.

Naming Consistency5/5

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.

Tool Count4/5

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.

Completeness4/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers