Notechondria

Version: 0.1.45 Build Date: 2026-04-19T16:00

What's Changed

Backend MCP: fill coverage gap against the REST API

Before this round, MCP exposed 21 tools covering profile, note CRUD, search, course CRUD, recent activity, heatmap, planner event create/update, note version list/snapshot, and attachment list. A full audit of backend/notechondria/api_urls.py against the tool registry surfaced 15+ user-facing actions that had no MCP equivalent. This round closes that gap so an MCP client can drive the same flows a human user drives in the Flutter apps.

New MCP tools (16)

All live in backend/mcp/tools.py and follow the existing (user, creator, params) -> dict contract.

Recycle bin:

  • list_deleted_notes — notes currently in the recycle bin (RecycleBinEntry rows whose note still has deleted_at).
  • restore_deleted_note — pairs with the existing soft-delete delete_note to give the full revert flow.
  • empty_recycle_bin — permanently purges every bin entry.

Note lookup + history:

  • get_note_by_uuid — deep-link / share-link resolution. Mirrors NoteByUuidApiView: full detail for the owner; public or default-course notes only for other users.
  • restore_note_version — completes the version history loop (list_note_versions + snapshot_note existed; restore didn't). Auto-snapshots the current state first with reason=before_restore_mcp so every restore is itself undoable.

Course subscriptions + ordering:

  • subscribe_course — adds an active CourseSubscription row and appends a CourseOperationLog entry (SUBSCRIBE type), mirroring CourseSubscribeApiView.post.
  • unsubscribe_course — flips is_active=False and logs UNSUBSCRIBE.
  • reorder_courses — rewrites sort_order for non-default courses from a supplied id list. Garbage entries and unowned ids are silently skipped (same tolerance as CourseReorderApiView).
  • list_course_notes — per-course note listing; non-owners only see public notes and default-course notes, matching the REST view's Q(is_public=True) | Q(course_id__is_default=True) gate.

Planner event lifecycle:

  • delete_event — the missing third corner of create/update/delete. PlannerEventDetailApiView never exposed delete via REST, so this actually adds new backend surface — MCP now supports the full CRUD triangle while REST still has only create + update. We'll reconcile by adding the REST delete later.

Activity:

  • get_activity — recent-notes list with configurable limit (default 10, cap 50).
  • get_activity_week — 7-day planner window (sessions + events + calendar feed entries + deadlines) via calendar_week_payload. Accepts an optional start_date ISO param.

Note activity sessions:

  • list_note_sessions — optionally filtered by note_id.
  • create_note_session — starts a new session at server-now.
  • end_note_session — sets ended_at=now() with optional title / summary overrides.

Calendar feeds:

  • list_calendar_feeds, create_calendar_feed, update_calendar_feed, delete_calendar_feed — full CRUD for iCal imports + subscribed URLs. create/update normalize share-URL forms via normalize_calendar_url the same way CalendarFeedListCreateApiView does.

Attachments:

  • delete_attachment — pairs with existing list_attachments.

Each new tool has a JSON schema, a docstring describing when to use it, and a test case in backend/mcp/tests.py exercising the happy path (and, where relevant, the cross-user permission boundary).

Tool coverage summary after this round

DomainTools (before → after)
Profile2 → 2
Notes6 → 6
Note versions2 → 3 (+restore)
Note UUID lookup0 → 1
Recycle bin0 → 3
Attachments1 → 2 (+delete)
Courses4 → 4
Course subs/order0 → 4
Planner events2 → 3 (+delete)
Calendar feeds0 → 4
Note sessions0 → 3
Activity/heatmap2 → 4 (+get_activity, +get_activity_week)
Total21 → 37

TODO.md maintenance

  • Removed the "course should have an independent app folder" entry — landed in 0.1.43.
  • Collapsed the long Attachment CDN rework spec. The three-commit frontend plan (shared store + editor wiring + planner/portal parity + list sheet) shipped across 0.1.40 / 0.1.41 / 0.1.42. Two deferred follow-ups remain in the file with pointers to their detailed specs in docs/versions/0.1.42.md: IndexedDB web backend and storage-budget UI surface.

Files Changed

New

  • docs/versions/0.1.45.md (this file).

Modified

  • VERSION: 0.1.44 → 0.1.45.
  • backend/mcp/tools.py: 16 new register_tool blocks + imports. Local _append_course_operation helper to avoid pulling notes.api.append_course_operation (and its module graph).
  • backend/mcp/tests.py: 12 new test methods exercising recycle bin lifecycle, note UUID owner/foreign split, version restore, subscribe/unsubscribe round-trip, course reorder, course-notes listing, planner event delete, activity + activity_week, note-session start/list/end, calendar-feed CRUD, and attachment-delete not-found error path.
  • docs/TODO.md: removed the course-split entry; collapsed the attachment-CDN rework.

Verification

  • python manage.py test notes creators mcp (SQLite in-memory via settings_test) — 130 tests pass (up from 118 in 0.1.43; +12 from this round).
  • python manage.py makemigrations --dry-run — no migration drift from this round. (The pre-existing alter_noteattachment_id drift from 0.1.37 is unrelated.)
  • python manage.py check — 0 issues.

Notes / follow-ups

  • Planner event REST delete. MCP now supports delete_event, but the REST API still doesn't. A future round should add DELETE to PlannerEventDetailApiView so the Flutter clients can drive the same flow an MCP agent already can. That also simplifies the planner's completed-events UX — right now the client marks as completed and hides, with no real delete path.
  • Block-level MCP tools (create/update/delete/reorder blocks) were considered and deferred. Block mode is a legacy editor surface (editor_mode='B'); most notes are 'G' or 'P'. If a user adopts block mode heavily, revisit.
  • Tool naming symmetry. Two small inconsistencies surfaced during the audit: get_recent_activity predates get_activity and they overlap (both return recent notes, differing only in payload shape). Deprecate or alias one in a later pass. Similarly, get_heatmap and get_activity_week both serve planning views but return different shapes — document which to use when in the MCP tool descriptions.