Skip to content

JamieLittle16/module-notes-mcp

Repository files navigation

module-notes-mcp

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.

Features

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

MCP Tools

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.

How It Works

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
Loading
  1. On startup, the server clones (or pulls) the configured Git repo and indexes all Markdown notes.
  2. Notes are parsed, chunked, and embedded via Cohere's embed-english-v3.0.
  3. The vector index is cached to disk as index-cache.json for fast restarts.
  4. An Express server exposes the MCP endpoint via Streamable HTTP transport.
  5. Every 90 minutes, the repo is re-synced and the index is rebuilt.

Quick Start

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

Local Dev (with a local notes checkout)

# Point LOCAL_REPO_PATH at your website repo checkout
echo 'LOCAL_REPO_PATH=/path/to/website' >> .env
npm run dev

Docker

docker build -t module-notes-mcp .
docker run -p 3000:3000 --env-file .env module-notes-mcp

Production (Ubuntu VM)

sudo bash scripts/setup-oci.sh mcp.yourdomain.com

This installs Node.js 22, clones the repo, sets up nginx + Let's Encrypt HTTPS, and configures a systemd service.

Environment Variables

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)

Content Sources

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.

Architecture

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)

Deployment

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.

License

MIT

About

MCP server for config-driven semantic note retrieval with vector search

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors