Skip to main content
Glama
snyderandrew35

Tutor Lesson-Script & Transcript Retrieval MCP

Tutor Lesson-Script & Transcript Retrieval MCP Server

A minimal remote MCP server exposing one tool, get_lesson_script, that returns the markdown tutor script for a lesson from ps_lessons.script on your Supabase project.

Retrieval only — no scoring or rubric logic.

The tool

get_lesson_script(lesson: str) -> str

Accepts a lesson code ("401.1", "413.2"), a concept ("Suffix s", "sh"), or a name ("Orientation", "Unit 1 Recap", "Unit 3 Passwords and Play"). It is scoped to curriculum_id = your-curriculum-uuid.

Behavior:

  • One match → returns that lesson's markdown script.

  • No match → a short message with the closest lesson names.

  • Several matches (e.g. "401" → 401.1 and 401.2) → lists them and asks you to be specific.

  • Match but empty script → says the lesson exists but isn't populated yet.

Related MCP server: Meetings MCP Server

Environment variables

Variable

Purpose

SUPABASE_URL

https://your-project-ref.supabase.co

SUPABASE_SERVICE_ROLE_KEY

Supabase → Project Settings → API → service_role key. Server-side only.

CURRICULUM_ID

The curriculum UUID this tool is scoped to (set your own).

CACHE_TTL

Optional; seconds to cache lesson rows (default 60).

Copy .env.example to .env and fill in the key for local runs.

Run locally

pip install -r requirements.txt
export $(grep -v '^#' .env | xargs)   # or set the vars however you prefer
python server.py                      # serves Streamable HTTP at http://localhost:8000/mcp

Test it with the MCP Inspector (no code needed):

npx @modelcontextprotocol/inspector
# Transport: Streamable HTTP   URL: http://localhost:8000/mcp
# then call get_lesson_script with {"lesson": "401.1"}

(Opening /mcp in a browser returns HTTP 406 — that's expected; it needs MCP headers.)

Deploy on Render

  1. Push this repo to GitHub.

  2. Render dashboard → New → Blueprint, point at the repo (uses render.yaml). Or New → Web Service with build pip install -r requirements.txt and start python server.py.

  3. Set env vars SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, and CURRICULUM_ID in the Render dashboard.

  4. Deploy. Your endpoint is https://<service-name>.onrender.com/mcp.

Free Render instances sleep after inactivity and cold-start in ~30–60s. Hit the endpoint once to warm it before a live demo, or use a paid instance.

Add as a Claude custom connector

In Claude → Customize → Connectors → + → Add custom connector:

  • Name: Tutor Lesson Scripts

  • URL: https://<service-name>.onrender.com/mcp

  • Leave OAuth blank (this server is unauthenticated — see note below).

Then enable it per chat via the + button → Connectors, and ask, e.g., "Use Tutor Lesson Scripts to get the script for lesson 401.1."

(On Team/Enterprise plans an Owner adds the connector under Organization Settings → Connectors first, then members connect.)

Demo suggestion

Warm the endpoint, then in a fresh chat: "Get me the tutor script for 418.1." → Claude returns the markdown (spell step reads hills → jets → tan → call, etc.). Pick and rehearse one lesson beforehand so the demo is smooth.

Security note

This server is unauthenticated for a dev demo: anyone with the URL can read lesson scripts, and the service-role key stays on the server (never sent to clients). Before anything beyond dev, put it behind OAuth (Claude custom connectors support an OAuth Client ID/Secret) or a gateway, and/or use a restricted key.


Phase 3: Transcript retrieval + Google auth

Two read-only PencilSpaces tools, and Google OAuth restricting the whole server (all three tools) to @yourdomain.org.

New tools

  • list_recordings(start_date, end_date) — recordings in a date range, each labeled with its lesson name (space title). Dedupes space lookups. Returns structured rows: recordingId, spaceId, lessonName, date, durationMinutes, hostUserIds.

  • get_transcript(recording_id, space_id=None) — the diarized transcript plus lesson name and host userId(s). status is one of ready | processing | empty | legacy | invalid_id | not_authorized | error. Segments are diarized by opaque userId (no name resolution — the eval skill corrects PS misattributions).

Flow: list_recordings → user picks one → get_transcript → the tutor-eval skill calls get_lesson_script. Pass spaceId from the list into get_transcript so it doesn't re-fetch the space.

Two auth layers

  • User → server: Google OAuth, gated to @yourdomain.org (below).

  • Server → PencilSpaces: PENCIL_SPACES_TOKEN Bearer secret. The token's account must be a host of the spaces, or the endpoints 401.

New environment variables

SERVER_BASE_URL (this server's public HTTPS URL), GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET, ALLOWED_EMAIL_DOMAIN (e.g. yourdomain.org), JWT_SIGNING_KEY (random, stable), PENCIL_API_BASE, PENCIL_SPACES_TOKEN.

Google Cloud OAuth setup

  1. In the yourdomain.org Google Workspace, create/select a Google Cloud project (must be under that org so "Internal" is available).

  2. APIs & Services → OAuth consent screen → Internal (this is the primary domain gate — only @yourdomain.org accounts can complete sign-in). Add scopes openid and .../auth/userinfo.email.

  3. APIs & Services → Credentials → Create credentials → OAuth client ID → Web application. Set:

    • Authorized JavaScript origins: https://your-service-name.onrender.com

    • Authorized redirect URI: https://your-service-name.onrender.com/auth/callback (must match exactly; /auth/callback is FastMCP's default).

  4. Copy the Client ID (…apps.googleusercontent.com) and Secret (GOCSPX-…) into Render env vars. The server allows Claude's callback (https://claude.ai/api/mcp/auth_callback) as a client redirect.

PencilSpaces

Set PENCIL_API_BASE (confirm the exact API host) and PENCIL_SPACES_TOKEN (a host account's Bearer token) as Render secrets. Read-only; never writes.

Connector re-connection

Turning on auth changes the existing connector: users must re-add / re-authorize it in Claude and sign in with Google. That's expected — it's the gate for handling real transcripts.

Testing with auth on

Use the MCP Inspector's OAuth flow, or the FastMCP client: Client("https://…onrender.com/mcp", auth="oauth"). First connect opens Google login.

Verify against a real recording (one-time)

The PencilSpaces response field names in pencilspaces.py follow the phase spec but were not verified against a live payload. On the first real recording, confirm:

  • the recordings list key (recordings) and fields (recordingId, spaceId, startingTime, duration);

  • transcript state markers (code/status == TRY_AGAIN_LATER; segments null vs []);

  • that the host userId from the space's hosts array matches the userId used in transcript segments (this overlap is what the role-tagging rests on). If the host never spoke or there are multiple hosts, note it — don't try to solve it now.

Privacy (by design)

userId pass-through, no name resolution — names never enter Claude. Real student transcripts: run evaluations in your own account and delete prototype conversations when done; no shared/retained transcript stores.

Related MCP Connectors

Related MCP Servers