Skip to content

v0.2.0: workspace redesign, coursework tracking, and MCP server#15

Merged
alexmodrono merged 18 commits into
mainfrom
worktree-release-0.1
Jun 1, 2026
Merged

v0.2.0: workspace redesign, coursework tracking, and MCP server#15
alexmodrono merged 18 commits into
mainfrom
worktree-release-0.1

Conversation

@alexmodrono

Copy link
Copy Markdown
Owner

v0.2.0

A workspace redesign plus a local MCP server that lets AI assistants query
synced coursework without re-downloading it.

Workspace & UI

  • Document-style course gallery that respects tag sections, hides disabled
    courses, and keeps the sidebar list alongside it.
  • Moodle cover images pulled from overviewfiles, cached per site.
  • Redesigned course detail with an elastic stretchy header and a correctly
    displayed course title.
  • Inline visual settings editor in place of the old plain form.
  • Toolbar button disambiguation and assorted macOS-convention polish.
  • Replaced WhatsNewKit with a custom What's New showcase and a dedicated,
    reusable Connect to AI window.

Coursework tracking

  • Read-only Moodle tracking: assignments/deadlines, submission status, grades,
    and quizzes (mod_assign, gradereport_user, mod_quiz).
  • New persistence tables (schema v12 covers, v13 tracking); bounded-concurrency
    refresh that never overwrites good data on a transient failure.

MCP server (FindleMCP)

  • Bundled helper inside the app that exposes synced content over the Model
    Context Protocol (14 tools): catalog, full-text search (FTS5,
    diacritic-insensitive), semantic search (NLEmbedding), read/extract, Moodle
    links, deadlines/grades/quizzes, and trigger_sync.
  • stdio transport by default; an optional Streamable HTTP transport with
    bearer-token auth for connecting ChatGPT via a tunnel (trigger_sync
    excluded from HTTP).
  • One-click registration with Claude Desktop and Claude Code, with
    already-registered detection.
  • Materialization is brokered by the File Provider — no custom IPC.
  • 29 automated tests (FindleMCPTests).

Release plumbing

  • The release workflow now Developer ID-signs the bundled MCP helper with the
    same team as the frameworks it loads, so the hardened runtime's library
    validation accepts it and notarization passes.

Introduce Debug app/file-provider xcconfigs and a gitignored
Signing.local.xcconfig (with a committed .example) so local builds sign
with a developer team without re-selecting it after each xcodegen run.
Align the Nightly and Release configs.
Add MoodleCourse.imageURL for the Moodle overview/banner image, and
new MoodleAssignment, MoodleGradeItem, MoodleQuiz, and MoodleQuizAttempt
models backing read-only coursework tracking.
Add a read-only open mode for the MCP server (read-write handle with
PRAGMA query_only for WAL visibility, no schema mutation), an image_url
column and per-course item fetch (schema v12), and tables plus
save/fetch operations for assignments, grades, quizzes, and quiz
attempts (schema v13).
Decode overviewfiles to surface the course cover image, and add
read-only tracking calls: assignments, submission status, grades,
quizzes, and quiz attempts.
Publish per-course sync state and downloaded cover images, group
courses by tag for the sidebar and gallery, expose course content
snapshots for the detail view, and refresh coursework tracking in the
background after each sync.
Move Settings to the standard window and fold Diagnostics into it, add
a Books-style course gallery with cover tiles, rebuild the course detail
view with an elastic cover header and an inline visual editor, surface
per-course sync status in the sidebar, and de-duplicate the sync toolbar
buttons.
A local stdio MCP server that exposes the synced Moodle corpus to
agents without manual uploads. Catalog tools (list_courses,
get_course_contents, search_items, get_item, get_moodle_url), on-demand
content reading and full-text search (read_item, search_text,
index_course), and read-only coursework tracking (list_deadlines,
get_submission_status, get_grades, get_quiz_attempts).

Reads the shared database read-only, materializes files through the
File Provider (the token stays in the extension), and keeps its own
FTS5 index so the app remains the single writer of the shared DB.
- refreshTracking: never persist a failed fetch (a transient network/
  token error would otherwise wipe stored deadlines/grades via the
  replace-all save); run independent per-item calls with bounded
  concurrency instead of serially.
- MCP read_item: distinguish an unreadable file from an empty one so a
  read failure is surfaced for retry rather than cached as empty text;
  disambiguate File Provider path matching by longest-prefix to avoid
  resolving a sibling course/folder.
- MCP search_text: CAST course_id for correct per-course filtering.
- Covers: percent-encode image URLs (accented/spaced filenames), key the
  cache file by site, and discard non-image downloads.
- Clear per-course caches on sign-out to prevent cross-site id collisions.
- Course detail: refresh content counts on app reactivation, restore the
  Hidden-course badge, and drop the per-frame DateFormatter allocation.
On-device embeddings via NaturalLanguage sentence models (zero
footprint, nothing downloaded). index_course now chunks and embeds each
file's text alongside the full-text index; semantic_search embeds the
query and ranks chunks by brute-force cosine similarity, returning the
most relevant passages. Embeddings are language-tagged so a query is
only compared against chunks in the same model's space.
The MCP server can't sync directly (no token, read-only DB), so the new
trigger_sync tool opens a findle://sync[?course=<id>] URL; the app — which
holds the token — handles it in onOpenURL and runs the sync (launching if
needed). Fire-and-forget: the agent re-queries after the app finishes.
- Bundle FindleMCP inside the app (Contents/MacOS) with a runpath to the
  app's embedded frameworks, so it ships with the .dmg/Homebrew app and
  Claude can point at a stable path.
- Add a one-click Claude integration: register the bundled MCP server
  with Claude Desktop and Claude Code by merging a findle entry into
  their config files (preserving all other keys), passing the app's real
  database path so the helper reads the right App Group. Permitted by a
  scoped temporary-exception entitlement; falls back to copying the
  snippet to the clipboard if the sandboxed write is blocked.
- Replace WhatsNewKit with a custom, animated What's New showcase that
  hosts the Connect-to-Claude actions and supports richer content.
Run the MCP server over HTTP (for tunnelling to remote clients like
ChatGPT) with: --http <port> --token <secret> (token may also come from
FINDLE_MCP_TOKEN). stdio remains the default.

A minimal Network.framework listener bound to 127.0.0.1 serves JSON-RPC
over HTTP POST (no SSE; GET returns 405), gated by a bearer token —
refuses to start without one. The tool dispatch is now shared between
the stdio and HTTP front-ends. trigger_sync is excluded from the HTTP
surface since it's a local side effect a remote client can't observe.
Add ClaudeIntegration.isInstalled(_:) (reads the config for an existing
findle entry) and reflect it in the What's New buttons: they show
'Added to …' with a checkmark when present and report 'Updated' on
re-add, instead of always offering a fresh 'Add'.
A reusable window (same visual language as What's New) for connecting
the bundled MCP server to assistants, openable any time from the menu
bar and Settings — not just from the one-time What's New sheet.

- Claude: the one-click Add/Added buttons for Desktop and Code.
- ChatGPT: a friendly guided setup — a generated bearer token (with
  regenerate), a copy-ready Terminal command pinned to the app's helper
  path and database, the ngrok command, and the steps to add it in
  ChatGPT. Copy buttons throughout.
29 unit tests covering the MCP server logic, compiled into a test bundle
(the @main entry is excluded). Covers: catalog + tracking tools against
a seeded temp database, the FTS5 index and embedding round-trips, the
embedder's chunking and cosine, text extraction and File Provider path
disambiguation, and HTTP request parsing. Makes IndexStore's path
injectable and opens two helpers for testing.
The helper was ad-hoc-signed while the app's embedded frameworks are
team-signed; the hardened runtime's library validation then refuses to
load them (different Team IDs), so Claude Code's launch failed with a
dyld error. Give FindleMCP a config that pulls in the local Team ID (via
the gitignored Signing.local.xcconfig) so it signs with the same team.
The Release archive builds FindleMCP as a standalone tool target before the
app embeds it. On CI there is no Signing.local.xcconfig, so MCP.xcconfig
resolved to the Automatic/Apple Development fallback, which the runner can't
satisfy and the archive failed. Write a Manual Developer ID config for the
helper so it signs with the same team as the frameworks it loads.
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown

Nightly Build

Download Findle Nightly (unsigned)

Built from 667a03b.

Important

This build is unsigned. macOS will block it on first launch. To open it:

  1. Try to open the app normally — macOS will show a warning and refuse.
  2. Go to System Settings → Privacy & Security, scroll down, and click Open Anyway.
  3. The File Provider extension requires code signing and won't work in this build.

@alexmodrono alexmodrono merged commit de87b78 into main Jun 1, 2026
1 check passed
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