Skip to content

Add junction MCP scope with schedule tools for PrincetonCourses#160

Merged
DIodide merged 2 commits intomainfrom
feat/junction-mcp-schedules
Mar 27, 2026
Merged

Add junction MCP scope with schedule tools for PrincetonCourses#160
DIodide merged 2 commits intomainfrom
feat/junction-mcp-schedules

Conversation

@DIodide
Copy link
Copy Markdown
Member

@DIodide DIodide commented Mar 26, 2026

Summary

  • Adds a new /junction/mcp endpoint that enables PrincetonCourses users to access and manage their TigerJunction schedules via the chat AI
  • Identity bridge: Princeton CAS NetID → Supabase RPC → UUID, with ownership checks on every operation
  • Read tools: get_user_schedules, get_schedule_details, verify_schedule
  • Write tools: create_schedule, add_course_to_schedule, remove_course_from_schedule, rename_schedule, delete_schedule
  • search_courses gains a scheduleId filter that excludes courses conflicting with an existing schedule — combines all existing search filters with conflict checking
  • ask-gateway routes to /junction/mcp when a netid is present in the request, passing x-user-netid header

Security

  • NetID injected server-side by PrincetonCourses backend (CAS session) — cannot be spoofed by client
  • Engine MCP protected by bearer token (shared secret)
  • All schedule tools verify schedule.user_id === resolved UUID before any read or write
  • Supabase service role key only on EC2, never exposed to browser
  • get_user_id_by_netid RPC uses SECURITY DEFINER to access auth.users

Test plan

  • Deployed to EC2 and tested end-to-end via PrincetonCourses dev
  • Verify get_user_schedules returns correct schedules for authenticated user
  • Verify add_course_to_schedule creates association with proper metadata
  • Verify search_courses with scheduleId excludes conflicting courses
  • Verify write tools reject operations on schedules not owned by the user
  • Verify non-schedule tools (courses, evals, instructors) still work without NetID

…tonCourses integration

Enables PrincetonCourses users to access and manage their TigerJunction
schedules via the chat AI, using their Princeton NetID as the identity bridge.

Engine changes:
- Add Supabase plugin (SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY)
- New /junction/mcp endpoint with "junction" scope
- junction-schedules.ts: read tools (get_user_schedules, get_schedule_details,
  verify_schedule) + write tools (create_schedule, add_course_to_schedule,
  remove_course_from_schedule, rename_schedule, delete_schedule)
- Identity: NetID → Supabase RPC get_user_id_by_netid → UUID
- search_courses: add scheduleId filter for conflict-free course search
- All tools enforce ownership (schedule.user_id === resolved UUID)

Ask-gateway changes:
- Accept netid in request body, route to /junction/mcp when present
- Send x-user-netid header to engine MCP
- Augment system prompt with schedule tool guidance
- Default to Fall 2026 (term 1272) in system prompt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eb16ba85b6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

.limit(fetchLimit);

// If scheduleId is provided and junction context available, post-filter for conflicts
if (scheduleId != null && junctionCtx) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject scheduleId when junction auth context is absent

search_courses accepts scheduleId in every MCP scope, but filtering only runs inside if (scheduleId != null && junctionCtx). Because createMcpServer only provides junctionCtx for the junction scope, calls from /mcp or /princetoncourses/mcp silently ignore scheduleId instead of erroring, which can return courses that actually conflict and mislead callers who relied on the documented filter behavior.

Useful? React with 👍 / 👎.

}

// Fetch more results when schedule filtering is active (some will be filtered out)
const fetchLimit = scheduleId ? resultLimit * 3 : resultLimit;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep result limit when schedule filtering cannot run

fetchLimit is increased whenever scheduleId is present, even before verifying that schedule filtering is available. In non-junction scopes this means the query returns up to limit * 3 rows without any post-filtering, violating the API limit contract (for example, limit=200 can return 600) and adding unnecessary DB load.

Useful? React with 👍 / 👎.

@DIodide DIodide self-assigned this Mar 26, 2026
@DIodide DIodide requested review from aikhanj and sgarimel March 26, 2026 21:19
@DIodide
Copy link
Copy Markdown
Member Author

DIodide commented Mar 27, 2026

and added tigersnatch

@DIodide DIodide merged commit 5951654 into main Mar 27, 2026
Copy link
Copy Markdown
Contributor

aikhanj commented Mar 27, 2026

#162 once this is merged we can mark as complete I believe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants