Skip to content

fix(mcp): repoint stored-degree tools at programs + auto-refresh JWT#12

Merged
lionelle merged 2 commits into
mainfrom
fix/mcp-programs-jwt
Jun 10, 2026
Merged

fix(mcp): repoint stored-degree tools at programs + auto-refresh JWT#12
lionelle merged 2 commits into
mainfrom
fix/mcp-programs-jwt

Conversation

@lionelle

@lionelle lionelle commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

First of three phases addressing the MCP tester feedback report. This phase ships the two critical fixes that block the stored-analysis workflow added in v0.5.0.

Issue A — search_degrees / get_degree / compare_degrees returned empty

The tester guessed RLS; the real cause was a table mismatch. These read tools still queried the legacy degrees yaml-blob table (with columns that don't even exist on programs), while import_degree writes to the new programs table — so they returned 0 rows despite 3 imported programs.

  • Repointed all three reads at programs.
  • search_degrees gains the queryable dimensions degree_type / program_kind / discipline alongside unitid / cip_prefix / catalog_year.
  • get_degree precedence is now program_key (unique) → degree_id → natural key, and returns the lossless unified-JSON document (what analyze_degree / cache_yaml accept).
  • compare_degrees resolves stored ids against program_key then degree_id.
  • Legacy degrees table + shelved store_degree left untouched for back-compat.

Issue C (+ B) — JWT never refreshed; long sessions die at ~1h

The MCP server cached one JWT at startup and discarded the refresh token, so every DB tool (IPEDS included) died ~1h in, and a client re-login didn't help (only a restart did).

  • DbClient now holds the full AuthState behind an Arc<RwLock> and calls current_token() before each request: proactive refresh when expired (re-reading the on-disk auth file first, so a fresh db login is picked up mid-process) and reactive retry-once on a 401.
  • The reqwest client gets explicit request/connect timeouts so a stalled call fails fast instead of hanging to the 4-minute MCP ceiling (Issue B).

Verification

  • Unit tests added: client refresh paths (valid → no-op; expired-but-fresh-on-disk → adopt; no-refresh → fail fast) and programs row deserialization (locks the column contract).
  • Live: db status auto-refreshed a 6h-expired token and the read succeeded; MCP search_degrees() returns all 3 imported programs, the degree_type=BS dimension filter works, and get_degree(program_key=…) returns the full document.
  • cargo fmt --check, cargo clippy --all-targets --all-features -D warnings, cargo doc -D warnings, and cargo test --all-features all green.

Follow-up phases (separate PRs): Fix 3 (render_plan_graph hang) and Fix 4 (QoL: @-prefix fast-fail, cache lock scope, expired-handle error, recommended_max_plans).

🤖 Generated with Claude Code

Two critical fixes from MCP tester feedback that block the stored-analysis
workflow added in v0.5.0.

Issue A — search_degrees / get_degree / compare_degrees returned zero rows
even though import_degree had written programs. They still queried the legacy
`degrees` yaml-blob table (with columns that don't exist on `programs`). Repoint
all three reads at `programs`:
- search_degrees gains the queryable dimensions degree_type / program_kind /
  discipline alongside unitid / cip_prefix / catalog_year.
- get_degree precedence is now program_key (unique) -> degree_id -> natural
  key, and returns the lossless unified-JSON `document` for downstream tools.
- compare_degrees resolves stored ids against program_key then degree_id.
- Legacy `degrees` + shelved `store_degree` left untouched for back-compat.

Issue C (+ B) — the MCP server cached one JWT at startup and never refreshed
it, so every DB tool (IPEDS included) died ~1h in; a client re-login didn't
help. DbClient now holds the full AuthState behind an Arc<RwLock> and calls
current_token() before each request: it refreshes proactively when expired
(re-reading the on-disk auth file first, so a fresh `db login` is picked up
mid-process) and reactively retries once on a 401. The reqwest client also
gets explicit request/connect timeouts so a stalled call fails fast instead of
hanging to the 4-minute MCP ceiling (Issue B).

Verified live: `db status` auto-refreshed a 6h-expired token and read
succeeded; MCP search_degrees() returns all 3 imported programs and
get_degree(program_key=…) returns the full document.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The client module doc linked to `current_token`, a private method, which trips
rustdoc::private_intra_doc_links under the Documentation CI job's
RUSTDOCFLAGS=-D warnings. Demote it to a plain code span. Verified with a clean
`cargo doc --no-deps --all-features` under RUSTFLAGS/RUSTDOCFLAGS=-D warnings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lionelle lionelle merged commit 6591281 into main Jun 10, 2026
4 checks passed
@lionelle lionelle deleted the fix/mcp-programs-jwt branch June 10, 2026 00:11
@lionelle lionelle mentioned this pull request Jun 10, 2026
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.

1 participant