An MCP (Model Context Protocol) server that provides semantic search and retrieval over Cambridge Computer Science Tripos notes. Connects to OpenAI's ChatGPT MCP plugin, Claude, or any MCP-compatible client.
Live at: https://mcp.jl1t.uk/mcp — Cohere-powered semantic search.
- Semantic search — find notes by meaning, not just keywords. Cohere embeddings power the vector index.
- Module listing — browse all Cambridge CS modules with metadata (year, term, paper).
- Note retrieval — fetch full note content by slug.
- Auto-sync — pulls fresh notes from the source Git repo every 90 minutes.
- Stateless HTTP transport — fresh transport per request, supports multiple concurrent clients.
| Tool | Description |
|---|---|
search |
Semantic search across all notes. Returns top-K results with scores and snippets. |
get_note |
Retrieve a specific note by its slug with full body. |
list_modules |
List all available modules with year, term, paper, and note counts. |
list_notes |
List all notes, optionally filtered by module or source. |
graph TB
Client(("💬 MCP Client")) -->|"POST / GET /mcp"| Express
subgraph Express[Express Server]
MCP[MCP Router]
Auth[Auth Check<br/>?token=]
Tools[Tool Handlers<br/>search / get_note / list_*]
end
Express -->|query| Index("Vector Index")
Index -.->|embedd| Cohere[Cohere API]
Index --> MiniSearch[(MiniSearch<br/>in-memory)]
subgraph Sync[Periodic Sync ~90min]
Git[Git Repo Source] -->|clone / pull| Repo[(Git Repo)]
Git -->|parse| Index
end
classDef box fill:#1e293b,stroke:#334155,color:#e2e8f0
classDef proc fill:#2563eb,stroke:#3b82f6,color:#fff
classDef store fill:#059669,stroke:#10b981,color:#fff
classDef ext fill:#7c3aed,stroke:#8b5cf6,color:#fff
class Express,Auth,Tools,MCP box
class Client,Git proc
class Index,Repo store
class Cohere,MiniSearch ext
- On startup, the server clones (or pulls) the configured Git repo and indexes all Markdown notes.
- Notes are parsed, chunked, and embedded via Cohere's
embed-english-v3.0. - The vector index is cached to disk as
index-cache.jsonfor fast restarts. - An Express server exposes the MCP endpoint via Streamable HTTP transport.
- Every 90 minutes, the repo is re-synced and the index is rebuilt.
git clone https://github.com/JamieLittle16/module-notes-mcp.git
cd module-notes-mcp
cp .env.example .env # edit with your keys
npm install
npm run build
npm start# Point LOCAL_REPO_PATH at your website repo checkout
echo 'LOCAL_REPO_PATH=/path/to/website' >> .env
npm run devdocker build -t module-notes-mcp .
docker run -p 3000:3000 --env-file .env module-notes-mcpsudo bash scripts/setup-oci.sh mcp.yourdomain.comThis installs Node.js 22, clones the repo, sets up nginx + Let's Encrypt HTTPS, and configures a systemd service.
| Variable | Required | Description |
|---|---|---|
COHERE_API_KEY |
Yes | Cohere API key for embeddings (free tier works) |
GITHUB_REPO_URL |
Depends | Git repo URL to clone (used when LOCAL_REPO_PATH is not set) |
LOCAL_REPO_PATH |
Depends | Path to a local checkout (takes precedence over cloning) |
ENCRYPTION_PASSPHRASE |
No | Enables private notes/modules loading if set |
MCP_PRIVATE_TOKEN |
No | Token required in URL query (?token=...) to access private content |
PORT |
No | Server port (default: 3000) |
HOST |
No | Bind address (default: 0.0.0.0) |
WORKDIR |
No | Data directory for cloned repos (default: /tmp/module-notes-mcp/repos) |
POLL_INTERVAL_MINUTES |
No | Re-sync interval (default: 90, set to 0 to disable) |
The server reads from a Git repo containing Markdown notes and JSON module metadata. By default it's configured for the Cambridge CS Tripos notes at:
# config/sources.yaml
sources:
- name: cs-tripos
type: git-repo
config:
url: ${GITHUB_REPO_URL}
localPath: ${LOCAL_REPO_PATH}
branch: main
paths:
modules: src/content/modules/*.json
notes: src/content/moduleNotes/**/*.{md,mdx}
private:
enabled: ${ENCRYPTION_PASSPHRASE}
modules: src/content/private/modules/*.json
notes: src/content/private/moduleNotes/**/*.{md,mdx}Add additional sources to index notes from multiple repos. Each source runs through the same parse → embed → index pipeline.
src/
├── index.ts # Entry point: loads config, runs sync, starts HTTP server
├── config.ts # YAML config loader with ${ENV_VAR} interpolation
├── sync.ts # Orchestrates source fetching and index building
├── core/
│ └── types.ts # Note, Module, SearchResult, SourceConfig types
├── mcp/
│ ├── server.ts # Express + MCP SDK server, token auth middleware
│ └── tools.ts # MCP tool handlers (search, get_note, list_*, etc.)
├── parser/
│ ├── markdown.ts # Frontmatter + body parsing (gray-matter)
│ └── modules.ts # Module JSON parsing
├── sources/
│ ├── base.ts # SourcePlugin interface
│ ├── git-repo.ts # Git clone/pull + file walking source
│ ├── index.ts # Source registry
│ └── walk.ts # Recursive file walker
└── vector-store/
├── index.ts # MiniSearch vector index + Cohere embeddings
└── embeddings.ts # Cohere API client (batch embedding)
The live instance runs on an Oracle Cloud AMD VM (Ubuntu 24.04) behind nginx + Let's Encrypt HTTPS, with Cloudflare as a proxy. The systemd service pulls from the private website repo via SSH deploy key and re-indexes every 90 minutes.
MIT