v11.10.0
Special thanks to timkjr for two of the fixes below (#321, #323) and to alivirgo for porting a Stop-hook fix over from the GitHub mirror (#330), which cannot take pull requests directly.
MINOR release. Consolidation gets two correctness fixes this cycle, and one of them changes behavior under an existing config rather than just fixing a bug: MCP_CLUSTERING_ALGORITHM=dbscan without scikit-learn installed now raises ConsolidationError at consolidation time instead of silently falling back to simple clustering (#329) — see the Upgrade note below. The other closes the gap between what the five memory_consolidate time horizons document and what they actually did (#325). Three hook fixes round out the release: two from external contributor timkjr on hardcoded harvest timeouts and a settings-merge bug that could wipe a user's own hooks (#321, #323), and one authored by alivirgo porting a Stop-hook transcript-pairing fix from the GitHub mirror, which takes no PRs of its own (#330).
Upgrade note: if you have MCP_CLUSTERING_ALGORITHM set explicitly to dbscan or hierarchical and do not have scikit-learn installed, consolidation will now fail loudly with ConsolidationError instead of silently running simple clustering. Fix with pip install 'mcp-memory-service[clustering]', or set MCP_CLUSTERING_ALGORITHM=auto to let the engine pick what's actually available.
Fixed
- fix(consolidation): make every time horizon mean the window it documents (#325, closes #324). The
memory_consolidatetool description promises one window per horizon — 24 hours / 7 days / 30 days / 90 days / 365 days — and four of the five did something else: daily reached 2 days instead of 1, weekly and monthly applied no age filter at all and scanned the entire store, and quarterly/yearly kept only memories OLDER than their window, which is inverted. All five now filter to the window they document. The deaddeltaconfig field andfilter_memories_by_agewere removed, and the batch cap moved into_take_oldest_batchso it applies inside the window instead of across the whole store. Known follow-up, not fixed here: no horizon reaches past 365 days any more, so controlled forgetting can't see anything older than a year (#327). - fix(consolidation): make the configured clustering algorithm the one that runs (#329, closes #326).
MCP_CLUSTERING_ALGORITHMdefaults todbscan, but scikit-learn — whichSemanticClusteringEngineimportsDBSCANandAgglomerativeClusteringfrom — was declared in no extra anywhere, so the default was unsatisfiable and clustering silently ran thesimplefallback behind a single WARNING log line. Adds a[clustering]extra (also folded into[ml]), changes the default toauto, and namingdbscan/hierarchicalexplicitly without scikit-learn installed now raisesConsolidationErrorwith an install hint instead of degrading silently. The clustering health check now reports the algorithm actually in use rather than a hardcoded string; the other five engine health checks are still hardcoded literals, tracked separately as #328. - fix(harvest): stop hardcoded 30s/60s wrapper timeouts overriding
HARVEST_LLM_TIMEOUT(#321, timkjr).rewrite_sync()andrewrite_batch_sync()wrapped the async LLM call in aThreadPoolExecutorand cappedfuture.result()at a hardcoded 30s/60s, completely independent ofHARVEST_LLM_TIMEOUT, which only controlled the inner httpx client's own timeout. Raising the env var did nothing for either wrapper — a real LLM call slower than the hardcoded cap silently returnedNone, indistinguishable from a legitimate SKIP verdict. Both wrappers now derive their timeout from the configured budget, with a margin so the outer wrapper never fires before the inner client's own timeout would, and the wrapper timeout scales with provider-chain length. - fix(hooks): settings.json merge in
configure_claude_settings(#323, timkjr). Two bugs in how a plugin reinstall handled an existing~/.claude/settings.json: declining the permission-request hook deleted the entirePreToolUsekey instead of just that hook's own group, wiping out any otherPreToolUsehook a user had registered themselves (live on every unattended install, since the opt-out prompt defaults to declined when stdin isn't a TTY); and the merge check for whether to append new groups compared the combined set of an entire batch rather than each group individually, so one new group in a batch caused every group in it to be re-appended, duplicating ones already present. Both are now scoped to the individual group. - fix(hooks): pair the last assistant turn with the prompt that preceded it (#330, authored by alivirgo, reported by AndreLiotta). Ports a fix opened against the GitHub mirror, which cannot take merges directly.
parseTranscriptin the auto-capture Stop hook walked the transcript backwards twice, independently, to find the last user prompt and the last assistant answer — so the hook could pair the user's next prompt with the previous answer. Tool_result-only user envelopes are now also skipped during the scan.
Changed
- claude-hooks: bump plugin manifest to 1.0.4. Two of the fixes above (#323, #330) touch
claude-hooks/, and the Claude Code Marketplace caches the plugin by this version, so a hook fix shipped without a manifest bump never reaches an already-installed user.
Install: pip install mcp-memory-service==11.10.0 · Docker: doobidoo/mcp-memory-service:11.10.0 (and :11.10.0-slim)