Skip to content

v11.10.0

Choose a tag to compare

@doobidoo doobidoo released this 05 Sep 07:08
· 25 commits to main since this release

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_consolidate tool 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 dead delta config field and filter_memories_by_age were removed, and the batch cap moved into _take_oldest_batch so 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_ALGORITHM defaults to dbscan, but scikit-learn — which SemanticClusteringEngine imports DBSCAN and AgglomerativeClustering from — was declared in no extra anywhere, so the default was unsatisfiable and clustering silently ran the simple fallback behind a single WARNING log line. Adds a [clustering] extra (also folded into [ml]), changes the default to auto, and naming dbscan/hierarchical explicitly without scikit-learn installed now raises ConsolidationError with 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() and rewrite_batch_sync() wrapped the async LLM call in a ThreadPoolExecutor and capped future.result() at a hardcoded 30s/60s, completely independent of HARVEST_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 returned None, 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 entire PreToolUse key instead of just that hook's own group, wiping out any other PreToolUse hook 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. parseTranscript in 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)