OfferDesk — Internship tracker

Public repo×

An internship tracker that tells you who to follow up with today — and that an AI assistant can update for you over MCP.

18 MCP tools
so an AI assistant can run the tracker for you
  • 123test cases
  • 43REST API routes
  • 7architecture decision records
  • 12event kinds in an append-only log
OfferDesk Home on fictional demo data: a headline reading 'Three conversations have gone quiet, two new postings are waiting to be started, and Tidewater Insurance closes in 4 days', with Follow up and Deadlines lists below.
Home writes the day's priorities as one sentence, derived from the event log.
OfferDesk Applications table with saved-view tabs (Everything, Waiting to hear, Response times, Due this month), Columns and Filters controls, an Export button and rows of fictional companies with status, location, deadline and source.
Saved views over the report builder; Export gives the same table as CSV or Excel.
OfferDesk's command palette open over Home, listing actions such as Add an application and Go to Board, each with a keyboard shortcut.
Keyboard-first: the ⌘K palette reaches every action and page.

Code on GitHub ↗

The problem

During recruiting season, applications scatter across a spreadsheet, an inbox, LinkedIn and a folder of resume_final_v3.pdf files. The questions that matter get lost — who went quiet, what's due this week, which résumé went where — because a status column only knows where things stand now, not how they got there.

What I built

  • An event-sourced core. Every save, application, outreach, reply and interview is a new row in an append-only log; database triggers block edits and deletes. Status, follow-ups, response rate and the funnel are derived from that history, never stored.
  • One core, three ways in. A keyboard-first React dashboard with a ⌘K palette, a Fastify REST API (43 routes) and an MCP server (18 tools, 9 safe to auto-approve). Telling an assistant "add this posting to my tracker" just works, and duplicates are caught by URL or company + role.
  • A report builder. 20 columns including computed ones (days waiting, response time), saved views and presets; CSV/Excel exports come from the same report as the screen, and a view can be pushed to Google Sheets.
  • Hosted or local, same code. Vercel + Turso (hosted SQLite) + Clerk with a workspace per user, or a local file with no account — one async libSQL code path across 7 migrations.
  • Secure Google integration. OAuth with PKCE, the narrow drive.file scope, AES-256-GCM-encrypted tokens, and immutable document versions so each application records the exact résumé that went out.

How it works

  1. Dashboard · REST · MCP
  2. Commands
  3. Append-only event log
  4. Derived status + signals
  5. Views → CSV / Excel / Sheets

Stack

  • TypeScript
  • React 19
  • Vite
  • Fastify 5
  • libSQL / Turso
  • MCP SDK
  • Clerk
  • Vercel
  • zod
  • Vitest
  • Biome
  • GitHub Actions

Shows

Proof

Tests123 test cases in 13 files (2,620 lines)test files in the public repo
Interfaces43 REST routes · 18 MCP tools · 7 dashboard pagespublic repo
Data model12 event kinds · 8 pipeline stages · 4 append-only triggerspublic repo
Decisions7 ADRsdocs/decisions
Commits23git history

What I'd do next

Résumé tailoring per application and a Gmail connector that logs replies automatically.

Sep 2026 · Public, MIT-licensed. Demo visuals use fictional seed data only.

← Back to the desktop All projects