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 (RecycleBinEntryrows whose note still hasdeleted_at).restore_deleted_note— pairs with the existing soft-deletedelete_noteto 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. MirrorsNoteByUuidApiView: 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_noteexisted; restore didn't). Auto-snapshots the current state first withreason=before_restore_mcpso every restore is itself undoable.
Course subscriptions + ordering:
subscribe_course— adds an activeCourseSubscriptionrow and appends aCourseOperationLogentry (SUBSCRIBEtype), mirroringCourseSubscribeApiView.post.unsubscribe_course— flipsis_active=Falseand logsUNSUBSCRIBE.reorder_courses— rewritessort_orderfor non-default courses from a supplied id list. Garbage entries and unowned ids are silently skipped (same tolerance asCourseReorderApiView).list_course_notes— per-course note listing; non-owners only see public notes and default-course notes, matching the REST view'sQ(is_public=True) | Q(course_id__is_default=True)gate.
Planner event lifecycle:
delete_event— the missing third corner of create/update/delete.PlannerEventDetailApiViewnever 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 configurablelimit(default 10, cap 50).get_activity_week— 7-day planner window (sessions + events + calendar feed entries + deadlines) viacalendar_week_payload. Accepts an optionalstart_dateISO param.
Note activity sessions:
list_note_sessions— optionally filtered bynote_id.create_note_session— starts a new session at server-now.end_note_session— setsended_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/updatenormalize share-URL forms vianormalize_calendar_urlthe same wayCalendarFeedListCreateApiViewdoes.
Attachments:
delete_attachment— pairs with existinglist_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
| Domain | Tools (before → after) |
|---|---|
| Profile | 2 → 2 |
| Notes | 6 → 6 |
| Note versions | 2 → 3 (+restore) |
| Note UUID lookup | 0 → 1 |
| Recycle bin | 0 → 3 |
| Attachments | 1 → 2 (+delete) |
| Courses | 4 → 4 |
| Course subs/order | 0 → 4 |
| Planner events | 2 → 3 (+delete) |
| Calendar feeds | 0 → 4 |
| Note sessions | 0 → 3 |
| Activity/heatmap | 2 → 4 (+get_activity, +get_activity_week) |
| Total | 21 → 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_toolblocks + imports. Local_append_course_operationhelper to avoid pullingnotes.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 viasettings_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-existingalter_noteattachment_iddrift 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 addDELETEtoPlannerEventDetailApiViewso 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_activitypredatesget_activityand they overlap (both return recent notes, differing only in payload shape). Deprecate or alias one in a later pass. Similarly,get_heatmapandget_activity_weekboth serve planning views but return different shapes — document which to use when in the MCP tool descriptions.