fix: MCP mutations now reliably notify the UI#67
Conversation
The WAL file watcher was unreliable for detecting external DB writes from the MCP stdio process — SQLite checkpoints could flush the WAL before fs.watch fired, causing tasks/projects/workflows/workspaces created via MCP to never appear in the UI. Added dbSignalChange() which writes a .db-signal file after every MCP mutation. The config-manager now watches for .db-signal, .db-wal, and .db changes (was only .db-wal). Debounce reduced from 1s to 300ms.
There was a problem hiding this comment.
Pull request overview
This PR improves cross-process UI refresh reliability by adding an explicit filesystem “signal” that the main server can watch to detect SQLite mutations performed by the MCP stdio process (avoiding missed events when relying on .db-wal alone).
Changes:
- Added
dbSignalChange()in the server database module to “touch” a.db-signalfile after MCP-driven mutations. - Updated
ConfigManager.watchDb()to watch.db-signal,.db-wal, and.dbwith a reduced debounce (1s → 300ms). - Updated MCP tool mutation handlers (tasks/projects/workspaces/workflows) to call
dbSignalChange()after DB writes.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/server/src/database.ts | Adds dbSignalChange() that writes/updates .db-signal in the config/db directory. |
| packages/server/src/config-manager.ts | Expands DB watcher to include .db-signal/.db and reduces debounce to improve UI update latency. |
| packages/mcp/src/tools/workspaces.ts | Calls dbSignalChange() after create/update/delete workspace mutations. |
| packages/mcp/src/tools/workflows.ts | Calls dbSignalChange() after create/update/delete workflow mutations. |
| packages/mcp/src/tools/tasks.ts | Calls dbSignalChange() after create/update/delete task mutations. |
| packages/mcp/src/tools/projects.ts | Calls dbSignalChange() after create/update/delete project mutations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| const WATCH_SUFFIXES = ['.db-signal', '.db-wal', '.db'] | ||
|
|
||
| try { | ||
| this.dbWatcher = fs.watch(DB_DIR, (eventType, filename) => { | ||
| if (!filename || !filename.endsWith('.db-wal')) return | ||
| if (!filename || !WATCH_SUFFIXES.some((s) => filename.endsWith(s))) return | ||
| // Debounce -- multiple writes can fire rapidly | ||
| if (this.debounceTimer) clearTimeout(this.debounceTimer) | ||
| this.debounceTimer = setTimeout(() => { | ||
| this.notifyChanged() | ||
| }, 1000) | ||
| }, 300) |
There was a problem hiding this comment.
Added tests in e3ced90 — covers all three suffix triggers (.db-signal, .db-wal, .db), unrelated file filtering, and debounce coalescing of rapid events into a single notification.
Cover the new watchDb behavior: .db-signal, .db-wal, and .db file detection, unrelated file filtering, and 300ms debounce coalescing.
Summary
fs.watch()on the SQLite WAL file was unreliable for detecting external DB writes from the MCP stdio process — SQLite checkpoints could flush the WAL before the watcher fired, causing MCP-created tasks/projects/workflows/workspaces to never appear in the UIdbSignalChange()todatabase.ts— writes a.db-signalfile after every MCP mutation as an explicit notification trigger.db-signal,.db-wal, and.dbchanges (was only.db-wal), with debounce reduced from 1s → 300msdbSignalChange()after writesTest plan
create_task) — verify it appears in the UI within ~300msupdate_task) — verify change reflects in UIdelete_task) — verify it disappears from UIfs.watchpicks up the.db-signalfile