# Changelog

**Recent releases for MCP Memory Service (v10.0.0 and later)**

All notable changes to the MCP Memory Service project will be documented in this file.

For older releases (v9.3.1 and earlier), see [docs/archive/CHANGELOG-HISTORIC.md](./docs/archive/CHANGELOG-HISTORIC.md).

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [10.10.1] - 2026-02-09

### Fixed
- **Search Handler (#444, #446):** Fixed AttributeError in memory_search - handle dict results correctly when storage backend returns dictionaries instead of SearchResult objects
- **Import Error (#443):** Fixed response_limiter import path in server/handlers/memory.py (max_response_chars feature now works)
- **Security (#441):** Added allowlist validation to maintenance scripts (SQL injection prevention in soft_delete_test_memories.py and migration scripts)

### Changed
- **Exact Search Mode (#445):** Changed to case-insensitive substring matching (LIKE) instead of full-content equality for more intuitive search behavior
  - **BREAKING CHANGE:** Users relying on exact full-match behavior may need to adjust queries
  - Previous: `mode=exact` matched only if entire content was identical
  - New: `mode=exact` performs case-insensitive substring search using SQL LIKE operator

### Known Issues
- **CLI Async (#447):** ingest-directory async handling under investigation - deferred to separate PR

## [10.10.0] - 2026-02-08

### Added
- **Environment Configuration Viewer**: New Settings Panel tab for comprehensive environment configuration visibility
  - **New API Endpoint**: `GET /api/config/env` returns 11 categorized parameter groups
  - **11 Configuration Categories**: Storage Backend, Database Settings, HTTP Server, Security, Quality System, Consolidation, OAuth, Embeddings, Logging, Graph Storage, Advanced Settings
  - **Security Features**: Sensitive value masking for API tokens, keys, and credentials
  - **User Experience**: Copy-to-clipboard functionality, dark mode optimization, organized accordion layout
  - **1405+ Lines**: 5 files modified (config_env.py, config.html, config.css, config.js, analytics.py)
  - **Use Cases**: Configuration troubleshooting, team onboarding, environment verification
- **Changelog Archival Agent**: New `changelog-archival` agent automates archival of older CHANGELOG entries when main file exceeds ~1000 lines
  - Automated version boundary detection and safe file splitting
  - Preserves all content in `docs/archive/CHANGELOG-HISTORIC.md`
  - Triggered after major version milestones or on explicit request
  - Maintains lean CHANGELOG focused on recent releases

### Enhanced
- **Graph Visualization Enrichment**: Added quality scores, updated timestamps, and metadata to graph nodes
  - Parse `metadata` JSON field for enriched node information (quality_score extraction)
  - Include `updated_at` timestamp for tracking node freshness
  - Increased max node limit from 500 to 1000 for larger graph visualization
  - Improved data completeness for analytics and visualization rendering

### Fixed
- **Installation Script Errors** (PR #439): Fixed three critical bugs preventing successful installation
  - Fixed NameError: `install_package` function now properly defined
  - Fixed ModuleNotFoundError: GPU detection uses `importlib.util` for direct file loading to avoid package __init__.py dependency issues during installation
  - Fixed Cloudflare API 401 error: Updated token verification to use account-specific endpoint with proper fallback to user-level endpoint
  - Added safety validations: spec/loader existence checks, account_id validation with `.strip()`
  - **Contributors:** @sykuang

## [10.9.0] - 2026-02-08

### Added
- **Batched Inference for Consolidation Pipeline** (PR #432): 4-16x performance improvement with GPU support
  - **GPU Performance**: 4-16x speedup on RTX 5050 Blackwell (1.4ms/item for batch=16, 0.7ms/item for batch=32 vs 5.2ms/item sequential)
  - **CPU Performance**: 2.3-2.5x speedup with batched ONNX inference
  - **Adaptive GPU Dispatch**: Automatically falls back to sequential processing for small batches (<16 items) to avoid padding overhead
  - **Configuration**:
    - `MCP_QUALITY_BATCH_SIZE` (default: 32) - controls batch size for quality scoring
    - `MCP_QUALITY_MIN_GPU_BATCH` (default: 16) - minimum batch size to use GPU (below threshold uses CPU sequential)
  - **New Methods**:
    - `ONNXRankerModel.score_quality_batch()` - batched DeBERTa classifier and MS-MARCO cross-encoder scoring
    - `QualityEvaluator.evaluate_quality_batch()` - two-pass batch evaluation with fallback strategy
    - `SqliteVecMemoryStorage.store_batch()` - batched embedding generation with atomic transaction handling
    - `SemanticCompressionEngine` - parallel cluster compression via `asyncio.gather`
  - **Backward Compatibility**: 100% backward compatible, set `MCP_QUALITY_BATCH_SIZE=1` for instant rollback
  - **Test Coverage**: 442 lines of new tests (17 tests in `test_batch_inference.py`)
  - **Contributors**: @rodboev

### Fixed
- **Token Truncation**: Fixed character-based [:512] truncation to proper tokenizer truncation (PR #432)
  - **Root Cause**: Classifier scoring truncated at 512 characters before tokenization, discarding ~75% of DeBERTa's 512-token context window (~2000 chars)
  - **Fix**: Enable `truncation(max_length=512)` at tokenizer initialization for all paths (classifier, cross-encoder, sequential, batch)
  - **Impact**: Better quality scores by utilizing full model context window
- **Embedding Orphan Prevention**: Fixed orphaned embeddings in `store()` and `store_batch()` methods (PR #432)
  - **Root Cause**: When embedding INSERT failed, fallback inserted without rowid, breaking `memories.id <-> memory_embeddings.rowid` JOIN
  - **Fix**: Wrap both memory INSERT and embedding INSERT in SAVEPOINT for atomicity - both succeed or both roll back
  - **Impact**: All memories now guaranteed to be searchable via semantic search
- **ONNX Float32 GPU Compatibility**: Cast model to float32 before ONNX export (PR #437)
  - **Root Cause**: DeBERTa v3 stores some weights in float16, producing mixed-precision ONNX graph that ONNX Runtime rejects
  - **Error Message**: "Type parameter (T) of Optype (MatMul) bound to different types (tensor(float16) and tensor(float))"
  - **Fix**: Add `model.float()` after `model.eval()` to cast all parameters to float32 before export
  - **Validation**: Tested on RTX 5050 Blackwell with PyTorch 2.10.0+cu128, ONNX Runtime 1.24.1 (CUDAExecutionProvider)
  - **Contributors**: @rodboev
- **Concurrent Write Stability**: Increased retry budget for WAL mode write contention (PR #435)
  - **Root Cause**: 3 retries with 0.1s initial delay (~0.7s total backoff) insufficient for multiple connections contending for SQLite RESERVED write lock
  - **Fix**: Bumped to 5 retries with 0.2s initial delay (~6.2s total backoff: 0.2+0.4+0.8+1.6+3.2)
  - **Impact**: `test_two_clients_concurrent_write` now passes consistently under WAL mode
  - **Contributors**: @rodboev

## [10.8.0] - 2026-02-08

### Added
- **Hybrid BM25 + Vector Search** (Issue #175, PR #436): Combines keyword matching with semantic search for improved exact match scoring
  - FTS5-based BM25 keyword search with trigram tokenizer for multilingual support
  - Parallel execution of BM25 and vector searches (<15ms typical latency)
  - Configurable score fusion weights (default: 30% keyword, 70% semantic)
  - Automatic FTS5 index synchronization via database triggers
  - Backward compatible: existing `mode="semantic"` searches unchanged
  - Available via unified search interface: `mode="hybrid"`
  - Configuration options:
    - `MCP_HYBRID_SEARCH_ENABLED`: Enable hybrid search (default: true)
    - `MCP_HYBRID_KEYWORD_WEIGHT`: BM25 weight (default: 0.3)
    - `MCP_HYBRID_SEMANTIC_WEIGHT`: Vector weight (default: 0.7)
  - Comprehensive test suite: 12 tests covering unit, integration, performance
  - Performance: <50ms average latency for 100 memories
  - Reference implementation: AgentKits Memory (sub-10ms latency, 70% token efficiency gains)

### Fixed
- **search_memories() Response Format** (PR #436): Corrected pre-existing bug where `search_memories()` returned Memory objects instead of dictionaries with `similarity_score`
  - Now returns flat dictionaries with all memory fields plus `similarity_score`
  - Updated affected tests (test_issue_396, hybrid search tests) to use dictionary access
  - Maintains API specification compliance
- **Test Safety: Prevent accidental Cloudflare data deletion** (Commit d3d8425): Tests now force `sqlite_vec` backend to prevent soft-deleting production memories in Cloudflare D1
  - Automatically overrides `MCP_MEMORY_STORAGE_BACKEND` to `sqlite_vec` for all test runs
  - Prints warning when overriding a cloud backend setting
  - Allows explicit cloud testing via `MCP_TEST_ALLOW_CLOUD_BACKEND=true`
- **Test Safety: Comprehensive safeguards to prevent production database deletion** (PR #438): Implemented triple safety system after incident on 2026-02-08 where test cleanup deleted 8,663 production memories
  - **Forced Test Database Path**: Creates isolated temp directory with `mcp-test-` prefix at module import time, forces `MCP_MEMORY_SQLITE_PATH` to test database
  - **Pre-Test Verification**: `pytest_sessionstart` hook verifies database is in temp directory, **aborts test run** if production path detected
  - **Triple-Check Cleanup**: `pytest_sessionfinish` validates (1) temp directory location, (2) no production indicators, (3) test markers present
  - **Critical Change**: Removed `allow_production=True` bypass flag - now relies on `delete_by_tag`'s own safety checks
  - **Additional Safeguards**: Backend forced to `sqlite_vec` unless explicitly allowed, verbose logging, explicit error handling
  - **Impact**: Defense in depth with 4 layers - environment override, pre-test abort, triple-check cleanup, backend-level safety
  - **Files Modified**: `tests/conftest.py` (+130 lines of safety code)
- **Dashboard Version Display**: Updated `src/mcp_memory_service/_version.py` to v10.8.0 (was showing v10.7.2 in dashboard)

### Added
- **Hybrid BM25 + Vector Search** (Issue #175, PR #436): Combines keyword matching with semantic search for improved exact match scoring
  - FTS5-based BM25 keyword search with trigram tokenizer for multilingual support
  - Parallel execution of BM25 and vector searches (<15ms typical latency)
  - Configurable score fusion weights (default: 30% keyword, 70% semantic)
  - Automatic FTS5 index synchronization via database triggers
  - Backward compatible: existing `mode="semantic"` searches unchanged
  - Available via unified search interface: `mode="hybrid"`
  - Configuration options:
    - `MCP_HYBRID_SEARCH_ENABLED`: Enable hybrid search (default: true)
    - `MCP_HYBRID_KEYWORD_WEIGHT`: BM25 weight (default: 0.3)
    - `MCP_HYBRID_SEMANTIC_WEIGHT`: Vector weight (default: 0.7)
  - Comprehensive test suite: 12 tests covering unit, integration, performance
  - Performance: <50ms average latency for 100 memories
  - Reference implementation: AgentKits Memory (sub-10ms latency, 70% token efficiency gains)

### Fixed
- **search_memories() Response Format** (PR #436): Corrected pre-existing bug where `search_memories()` returned Memory objects instead of dictionaries with `similarity_score`
  - Now returns flat dictionaries with all memory fields plus `similarity_score`
  - Updated affected tests (test_issue_396, hybrid search tests) to use dictionary access
  - Maintains API specification compliance
- **Test Safety: Prevent accidental Cloudflare data deletion** (Commit d3d8425): Tests now force `sqlite_vec` backend to prevent soft-deleting production memories in Cloudflare D1
  - Automatically overrides `MCP_MEMORY_STORAGE_BACKEND` to `sqlite_vec` for all test runs
  - Prints warning when overriding a cloud backend setting
  - Allows explicit cloud testing via `MCP_TEST_ALLOW_CLOUD_BACKEND=true`

## [10.7.2] - 2026-02-07

### Fixed
- **Server Management buttons cause page reload** (PR #429): Added `type="button"` to Check for Updates, Update & Restart, and Restart Server buttons in Settings modal. Without explicit type, buttons inside `<form>` default to `type="submit"`, causing unintended form submission and page reload.

## [10.7.1] - 2026-02-07

### Fixed
- **Test Script Security Hardening** (Issue #419, PR #427): Hardened test backup scripts with several security and robustness fixes:
  - **Command Injection (HIGH)**: Replaced unquoted heredoc with quoted `<< 'EOF'` + `printf '%q'` for safe path escaping
  - **Argument Injection**: Added `--` separators to `sqlite3` and `rm` commands
  - **Database Corruption Risk**: Replaced `cp` with `sqlite3 .backup` for atomic, WAL-safe backups
  - **Overly Broad pkill**: Narrowed pattern from `"memory server"` to `"mcp_memory_service"`
  - **Cross-Platform Documentation**: Added platform-specific path table (macOS/Linux/Windows) to README
  - **Test Fix**: Added auth dependency overrides to quality HTTP endpoint tests
- **Dashboard Authentication for API Calls** (Commit 5bf4834): Added authentication headers to all Dashboard API endpoints
  - **Frontend Fixes**: Replaced 19 direct `fetch()` calls with `this.apiCall()` for proper auth header handling
  - **Affected Tabs**: Manage, Analytics, Quality tabs now properly authenticate
  - **FormData Uploads**: Added auth headers to 2 document upload fetch calls
  - **API Middleware**: Added auth middleware to consolidation API (3 endpoints: trigger, status, recommendations)
  - **API Middleware**: Added auth middleware to quality API (5 endpoints: rate, evaluate, get, distribution, trends)
  - **API Overview Page**: Fixed `/api-overview` page to pass auth headers to `/api/health/detailed` fetch calls
  - **Root Cause**: Direct fetch() calls bypassed the apiCall() auth layer, causing 401 errors when API key authentication was enabled
  - **Files Changed**: 5 files modified (app.js, index.html, consolidation.py, quality.py, app.py)
  - **Impact**: All Dashboard features now work correctly with API key authentication enabled

## [10.7.0] - 2026-02-07

### Added
- **Backup UI Enhancements** (Issue #375, PR #375): Complete backup management interface with View Backups modal
  - **View Backups Modal**: Interactive backup history showing filename, size, date, and age
  - **Backup Directory Display**: Shows backup directory path in summary for easy file access
  - **API Enhancement**: Added `backup_directory` field to BackupStatusResponse API
  - **User Experience**: Cleaner, more informative backup management interface

### Fixed
- **Backup Form Controls**: Fixed backup buttons using `type="button"` to prevent unintended form submission
- **Event Binding**: Switched to inline onclick handlers for reliable event binding in settings modal
- **Toast Notifications**: Fixed toast pointer-events to ensure notifications appear over modals
- **Cache Busting**: Updated cache-busters for static assets to ensure proper browser refresh

## [10.6.1] - 2026-02-07

### Fixed
- **Dashboard SSE Authentication** (Issue #420, PR #423): Fixed EventSource API authentication for Server-Sent Events
  - **Root Cause**: Browser `EventSource` API does not support custom headers, causing 401 errors on `/api/events` endpoint
  - **Solution**: Pass authentication credentials as URL query parameters (`api_key=` and `token=` for OAuth)
  - **Implementation**: Used `URL` and `URLSearchParams` APIs for clean query parameter handling
  - **OAuth Support**: Added `token` query parameter support in auth middleware for SSE connections
  - **Security**: Added `<meta name="referrer" content="no-referrer">` to prevent API key leakage via HTTP Referer headers
  - **Impact**: Dashboard now maintains real-time SSE connection when authentication is enabled

## [10.6.0] - 2026-02-07

### Added
- **Server Management Dashboard** (PR #421): Complete server administration from Dashboard Settings
  - **REST API**: 4 new endpoints at `/api/server/*` (admin-protected):
    - `GET /api/server/status` - Real-time server status (version, uptime, platform info)
    - `GET /api/server/version/check` - Git-based update detection (commits behind origin)
    - `POST /api/server/update` - One-click git pull + pip install workflow
    - `POST /api/server/restart` - Safe server restart with 3-second delay
  - **Dashboard UI**: Server management section in Settings modal
  - **Security**: Admin-only access, explicit confirmation required, full audit logging

## [10.5.1] - 2026-02-06

### Added
- **Test Environment Safety Scripts** (Issue #419, PR #418): 4 critical scripts (524 lines) to prevent production database testing
  - **backup-before-test.sh**: Mandatory backup before testing with date-stamped archives and environment isolation
  - **setup-test-environment.sh**: Isolated test environment setup (port 8001) with separate data directory
  - **cleanup-test-environment.sh**: Safe test data cleanup with environment verification
  - **scripts/test/README.md**: Comprehensive testing workflow documentation (250 lines)
  - **Testing Workflow**: Complete development-to-deployment test lifecycle
  - **Safety Guarantees**: Environment validation prevents production database corruption
  - **Developer Experience**: Clear instructions for safe testing practices
  - **CRITICAL**: Prevents data loss incidents from running tests against production databases
  - Note: Security improvements (command injection fixes, atomic backups) tracked in Issue #419

## [10.5.0] - 2026-02-06

### Added
- **Dashboard Authentication UI** (Issue #414, Issue #410, PR #416): Comprehensive authentication detection and graceful user experience
  - **Authentication Detection**: Automatically detects authentication state on dashboard load (HTTP 401/403 responses)
  - **User-Friendly Modal**: Authentication modal with clear instructions for API key and OAuth flows
  - **API Key Authentication**: Secure input field with autocomplete=off, session storage, and automatic page reload after auth
  - **OAuth Flow**: Prominent "Sign in with OAuth" button for team collaboration
  - **Security Improvements**: HTTPS warning for production deployments, credential cleanup on state changes
  - **Dark Mode Compatible**: Fully styled authentication UI with dark mode support
  - **State Management**: Robust authentication state tracking across page loads
  - **Error Handling**: Clear error messages for authentication failures with retry guidance
  - **~400 lines** across 3 files: app.js (state management), index.html (modal UI), style.css (dark mode styling)
  - Resolves user confusion when accessing HTTP dashboard without authentication (Issue #410)
  - Provides discoverable authentication flow replacing raw 403 errors (Issue #414)

## [10.4.6] - 2026-02-06

### Changed
- **Documentation Enhancement** (Issue #410, Issue #414, PR #415): Clarified HTTP dashboard authentication requirements in README.md
  - Added authentication setup example to Document Ingestion section with `MCP_ALLOW_ANONYMOUS_ACCESS=true` for local development
  - Added prominent warning callout explaining authentication requirement by default
  - Documented all three authentication options in Configuration section:
    - **Option 1**: API Key authentication (`MCP_API_KEY`) - recommended for production
    - **Option 2**: Anonymous access (`MCP_ALLOW_ANONYMOUS_ACCESS=true`) - local development only
    - **Option 3**: OAuth team collaboration (`MCP_OAUTH_ENABLED=true`)
  - Improves first-time user experience by clarifying why dashboard returns 403 errors without authentication
  - Addresses user confusion when accessing HTTP dashboard with default secure configuration

### Fixed
- **CHANGELOG Cleanup** (PR f1de0ca): Removed duplicate v10.4.3 release entry from Previous Releases section

## [10.4.5] - 2026-02-05

### Added
- **Unified CLI Interface** (Issue #410, PR #261d324): `memory server --http` flag for starting HTTP REST API server
  - **Before**: Required manual script invocation: `python scripts/server/run_http_server.py`
  - **After**: Simple unified command: `memory server --http`
  - Direct uvicorn.run() integration (no subprocess overhead)
  - Respects `MCP_HTTP_PORT` and `MCP_HTTP_HOST` environment variables
  - Clear user feedback with URLs and graceful error handling
  - Better UX: Single command interface, easier to discover in --help

### Changed
- **Documentation Update** (PR #301ba2d): Updated README.md to use `memory server --http` instead of script invocation
  - Simplifies user workflow with unified command interface
  - Removes confusion about separate MCP vs HTTP server commands

## [10.4.4] - 2026-02-05

### Security
- **CRITICAL: Timing Attack Vulnerability** (PR #411): Fixed CWE-208 timing attack in API key comparison
  - Replaced direct string comparison with `secrets.compare_digest()` for constant-time comparison
  - Prevents attackers from determining correct API key character-by-character via timing analysis
  - All API key authentication methods now use secure comparison (X-API-Key header, query parameter, Bearer token)
  - Severity: CRITICAL - Recommend immediate upgrade for all deployments using API key authentication

### Fixed
- **API Key Authentication without OAuth** (Issue #407, PR #411): API key auth now works independently of OAuth configuration
  - Root cause: API routes had conditional OAuth dependencies that prevented API key authentication when `MCP_OAUTH_ENABLED=false`
  - Solution: Removed OAuth conditionals from ALL 44 API route endpoints
  - Authentication middleware now handles all auth methods unconditionally: OAuth, API key, or anonymous
  - Enables simple single-user deployments without OAuth overhead

### Added
- **X-API-Key Header Authentication** (PR #411): Recommended method for API key authentication
  - Usage: `curl -H "X-API-Key: your-secret-key" http://localhost:8000/api/memories`
  - More secure than query parameters (not logged in server logs)
  - Works with all API endpoints
- **Query Parameter Authentication** (PR #411): Convenient fallback for scripts/browsers
  - Usage: `curl "http://localhost:8000/api/memories?api_key=your-secret-key"`
  - Warning: Logs API keys in server logs - use X-API-Key header in production
- **Bearer Token Compatibility** (PR #411): Backward compatible with existing Bearer token auth
  - Usage: `curl -H "Authorization: Bearer your-secret-key" http://localhost:8000/api/memories`

### Changed
- **OAuth Import Behavior** (PR #411): OAuth authentication modules now always imported regardless of configuration
  - Previous behavior caused FastAPI AssertionError when `OAUTH_ENABLED=false`
  - Middleware decides auth method at runtime based on configuration
  - Allows server startup with OAuth disabled while maintaining proper authentication

## [10.4.3] - 2026-02-04

### Fixed
- **Consolidation Logger** (PR #404): Fixed NameError in consolidator.py where logger.warning() was called but no module-level logger existed
  - Added module-level logger import to prevent crashes in `_handle_compression_results` and `_apply_forgetting_results` methods
  - Ensures proper error logging during memory consolidation operations
- **Windows Task Scheduler HTTP Dashboard** (PR #402): Fixed 6 bugs preventing scheduled task from starting HTTP dashboard on Windows
  - Fixed Task Scheduler PATH issues by using full path resolution via $env:SystemRoot for powershell.exe
  - Added robust executable discovery (Find-Executable) that probes known uv/python install locations (`$env:USERPROFILE\.local\bin`, `$env:LOCALAPPDATA\uv`, `$env:USERPROFILE\.cargo\bin`)
  - Fixed $using:LogFile silent failure in .NET event handlers by changing to $script:LogFile for proper log capture
  - Corrected health check URLs from https:// to http:// (server defaults to HTTP)
  - Fixed display URLs in status output to show correct http:// protocol
  - Added executable path logging for easier Task Scheduler debugging
  - Improved Python version discovery with py.exe launcher and descending version sort (prefers latest)

## [10.4.2] - 2026-02-01

### Fixed
- **Docker Container Startup** (Issue #400): Fixed ModuleNotFoundError for aiosqlite and core dependencies
  - Docker container failed to start with "ModuleNotFoundError: No module named 'aiosqlite'"
  - Root cause: `uv pip install -e .${INSTALL_EXTRA}` was not properly installing core dependencies
  - Solution: Split Docker package installation into three clear steps:
    1. Install CPU-only PyTorch if needed (conditional)
    2. Always install core dependencies with `python -m uv pip install -e .`
    3. Install optional dependencies with `python -m uv pip install -e ".${INSTALL_EXTRA}"`
  - This ensures all core dependencies (aiosqlite, fastapi, sqlite-vec, etc.) are always installed
  - Preserved backward compatibility with all build argument combinations
  - Verified with testing: core dependencies properly installed in all configurations

## [10.4.1] - 2026-01-29

### Fixed
- **Time Expression Parsing** (Issue #396): Fixed `time_expr` parameter to correctly parse natural language time expressions
  - Changed from `extract_time_expression()` to `parse_time_expression()` in `search_memories()`
  - Now correctly handles: "last week", "3 days ago", "last 5 days", "1 week ago"
  - `extract_time_expression()` was designed for extracting time expressions from larger text queries
  - `parse_time_expression()` is the correct function for isolated time expressions
  - Added comprehensive regression tests covering reported failures and edge cases
  - ISO date workaround (`after`/`before` parameters) continues to work as before

## [10.4.0] - 2026-01-29

### Added
- **Semantic Deduplication** (Issues #390, #391): Prevents storing semantically similar content within configurable time window
  - New `_check_semantic_duplicate()` method in SQLiteVecStorage using KNN cosine similarity
  - Configurable via environment variables:
    - `MCP_SEMANTIC_DEDUP_ENABLED` (default: true) - Enable/disable feature
    - `MCP_SEMANTIC_DEDUP_TIME_WINDOW_HOURS` (default: 24) - Time window for duplicate detection
    - `MCP_SEMANTIC_DEDUP_THRESHOLD` (default: 0.85) - Similarity threshold (0.0-1.0)
  - Catches cross-hook duplicates (e.g., PostToolUse + SessionEnd reformulations)
  - Returns descriptive error messages: "Duplicate content detected (semantically similar to {hash}...)"
  - Efficient KNN search using sqlite-vec's `vec_distance_cosine()` function
  - Comprehensive test suite with 6 new tests covering time windows, configuration, and edge cases

- **Memory Budget Optimization** (Issue #390): Increased memory retrieval capacity and reserved slots for curated memories
  - Increased `maxMemoriesPerSession` from 8 to 14 slots (75% increase)
  - New `reservedTagSlots` configuration (default: 3) - Guarantees minimum slots for tag-based retrieval
  - Smart slot allocation across retrieval phases:
    - Phase 0 (Git): Up to 3 slots (adaptive)
    - Phase 1 (Recent): ~60% of remaining slots
    - Phase 2 (Tags): At least `reservedTagSlots`, more if available
  - Prevents semantic search from crowding out curated memories
  - Configuration documentation added to session-start.js

- **Enhanced Content Truncation** (Issue #392): Multi-delimiter sentence boundary detection
  - Expanded from 4 to 9-10 delimiter types: `. ` `! ` `? ` `.\n` `!\n` `?\n` `.\t` `;\n` `\n\n`
  - Improved break point algorithm preserves natural sentence boundaries
  - Lowered threshold from 80% to 70% for more flexibility
  - Applied consistently across auto-capture-patterns.js and context-formatter.js
  - Eliminates mid-sentence cuts at colons/commas when better delimiters present

### Changed
- **Tag Case-Normalization** (Issue #391): All tags stored in lowercase with case-insensitive deduplication
  - Updated `normalize_tags()` in memory_service.py to convert all tags to lowercase
  - Eliminates duplicate tags like `["Tag", "tag", "TAG"]` → `["tag"]`
  - Applied across all tag sources: parameter tags, metadata tags, hook-generated tags
  - JavaScript hooks updated for consistent case-normalization:
    - auto-capture-patterns.js: generateTags() normalizes all tags
    - session-end.js: Project name, language, topics, frameworks all lowercase
  - Comprehensive test suite with 11 new tests for unit and integration scenarios
  - Backward compatible: Existing mixed-case tags remain unchanged, searches already case-insensitive

- **Hook Deduplication Threshold** (Issue #391): Improved Jaccard similarity detection
  - Lowered threshold from 80% to 65% in context-formatter.js
  - Catches more cross-hook reformulations (55-70% similarity range)
  - Maintains balance between duplicate detection and legitimate variations

### Fixed
- **Test Environment Isolation**: Disabled semantic deduplication during tests
  - Prevents interference with existing test expectations
  - Tests can now create similar content without triggering dedup
  - Added clear documentation in conftest.py

### Documentation
- Added comprehensive implementation plan in fix-plan-issues-390-391-392.md
- Updated .env.example with semantic deduplication configuration
- Added 17 new tests with detailed documentation
- Created TEST_ADDITIONS_SUMMARY.md documenting all test scenarios

### Performance
- Semantic dedup adds <100ms overhead per storage operation
- KNN search leverages sqlite-vec's optimized cosine distance calculations
- No impact on retrieval performance
- Hook execution time unchanged (<10s total)

## [10.3.0] - 2026-01-29

### Added
- **SQL-Level Filtering Optimization** (#374): Dramatic performance improvements for large datasets
  - Optimized `delete_memories` path using SQL filtering instead of Python-level filtering
  - New `delete_by_tags` method for Cloudflare backend for efficient bulk deletion
  - New `get_memories_by_time_range` support for time-based filtering
  - Performance benchmarks demonstrating significant improvements:
    - **115x speedup** for tag filtering (1000 memories: 116ms → 1ms)
    - **74x speedup** for time range filtering (1000 memories: 36ms → 0.49ms)
    - **98% memory reduction** (10,000 memories: 147MB → 2.5MB)
  - Contributor: @isiahw1

### Fixed
- **API Consistency** (#393): Standardized `delete_by_tags` signature across all backends
  - All backends now return 3-tuple `(count, message, deleted_hashes)`
  - Prevents unpacking errors and provides audit trail for deleted content
  - Enhanced tracking for sync operations in hybrid backend
- **Enhanced Exception Handling**: Improved error handling in external embedding API
  - Specific JSONDecodeError catch for better error messages
  - Duplicate index detection and validation
  - Missing 'index' field validation in API responses
- **Test Fixes**: Corrected test class name typo in `test_external_embeddings.py`

## [10.2.1] - 2026-01-28

### Fixed
- **Integer Enum Incompatibility** (#387): Fixed OpenCode with Gemini model failure
  - Changed `memory_quality` tool `rating` parameter from integer enum to string enum
  - Added backwards-compatible conversion logic in quality handler
  - Resolves MCP client compatibility issues with integer enum values
  - Files: `server_impl.py`, `server/handlers/quality.py`
- **Wrong Method Name in delete_with_filters** (#389): Fixed delete operations with tag/time filters
  - Replaced non-existent `list_memories()` calls with correct `get_all_memories()` method name
  - Affected delete operations using tag or timeframe filters
  - Files: `storage/base.py` (2 locations)
  - All 8 delete tests now passing in `test_unified_tools.py`

## [10.2.0] - 2026-01-28

### Added
- **External Embedding API Support** (#386): Use external OpenAI-compatible embedding APIs (vLLM, Ollama, TEI, OpenAI) instead of local models
  - Configure via `MCP_EXTERNAL_EMBEDDING_URL`, `MCP_EXTERNAL_EMBEDDING_MODEL`, `MCP_EXTERNAL_EMBEDDING_API_KEY`
  - **Important**: Only supported with `sqlite_vec` backend (not compatible with `hybrid` or `cloudflare` backends)
  - Graceful fallback to local models if external API unavailable
  - Supports any OpenAI-compatible `/v1/embeddings` endpoint
  - Automatic dimension detection from API responses
  - Backend validation ensures correct configuration
  - 10/10 core tests passing (3 integration tests require refactoring)
  - See `docs/deployment/external-embeddings.md` for setup guide
  - Contributor: @isiahw1

## [10.1.2] - 2026-01-27

### Fixed
- **Windows PowerShell 7+ compatibility**: Fixed SSL certificate validation in `manage_service.ps1`
  - Extends previous fix from `update_and_restart.ps1` to `manage_service.ps1`
  - PowerShell 5.1: Uses `ICertificatePolicy` (.NET Framework)
  - PowerShell 7+: Uses `-SkipCertificateCheck` parameter on `Invoke-WebRequest`
  - Error was: `CS0246: The type or namespace name 'ICertificatePolicy' was not found`

## [10.1.1] - 2026-01-27

### Fixed
- **Missing `requests` dependency**: Added `requests>=2.28.0` to pyproject.toml (Fixes #378)
  - `sentence-transformers` requires `requests` but doesn't declare it as a dependency
  - Caused `ModuleNotFoundError: No module named 'requests'` during embedding initialization
  - Affects fresh installations without `requests` pre-installed
- **Windows PowerShell 7+ compatibility**: Fixed `update_and_restart.ps1` SSL certificate validation
  - PowerShell 5.1: Uses `ICertificatePolicy` (.NET Framework)
  - PowerShell 7+: Uses `-SkipCertificateCheck` parameter on `Invoke-RestMethod`
  - Script now detects PowerShell version and uses appropriate method
  - Error was: `CS0246: The type or namespace name 'ICertificatePolicy' was not found`

### Changed
- **Relationship Inference Threshold Tuning**: Improved relationship type diversity in memory graph analytics
  - Relaxed default minimum confidence threshold recommendation from 0.6 to 0.4 for existing graphs
  - Enables discovery of more nuanced relationship types (causes, fixes, contradicts, supports, follows)
  - Production results: Improved from 2 to 3 relationship types with 0.4 threshold
  - Distribution on 2,392 edges: related (75.52%), contradicts (18.09%), follows (6.39%)
  - Script usage: `python scripts/maintenance/update_graph_relationship_types.py --min-confidence=0.4`
  - Balances precision and recall - lower values (0.3) may introduce false positives
  - Note: Script default remains 0.6 for conservative new deployments

## [10.1.0] - 2026-01-25

### Added
- **Python 3.14 Support**: Extended Python compatibility to 3.10-3.14 (Fixes #376)
  - Upgraded tokenizers dependency from ==0.20.3 to >=0.22.2
  - Resolves PyO3 compatibility issues preventing installation on Python 3.14
  - Fixed tokenizers API change: `encode((query, text))` → `encode(query, pair=text)` in ONNX ranker
  - No breaking changes - maintains full backward compatibility
  - All 1005 tests passing across all supported Python versions
  - Enables adoption by projects requiring Python 3.14

## [10.0.3] - 2026-01-25

### Fixed
- **Backup scheduler critical bugs** (Fixes #375)
  - **Scheduler never started**: `BackupScheduler.start()` was never called in FastAPI lifespan
    - Added `backup_scheduler` global variable to `app.py`
    - Integrated scheduler startup into FastAPI lifespan context manager
    - Added graceful shutdown handling in lifespan cleanup
    - Automatic backups now work as intended
  - **Past dates in "Next Scheduled"**: `_calculate_next_backup_time()` returned past dates when server was offline longer than backup interval
    - Rewrote calculation logic with while loop to advance to future time
    - Now correctly handles multi-interval downtime (e.g., server down for 5 days with daily backups)
    - "Next Scheduled" field always shows a future timestamp
  - **Comprehensive testing**: Added 8 new tests covering hourly/daily/weekly intervals, past-due scenarios (5h, 10d, 4w overdue), and edge cases
  - Bug existed since commit 8a19ba8 (PR #233, Nov 2025)
  - Commit: 94f3c3a

## [10.0.2] - 2026-01-23

### Fixed
- **Tool list now shows only 12 unified tools**
  - Removed 20 deprecated tool definitions from MCP tool list advertisement
  - Deprecated tools still work via backwards compatibility routing in `compat.py`
  - Achieves the promised "64% tool reduction" (34→12 visible tools)
  - Claude Desktop and other MCP clients now see clean, focused tool list
  - No breaking changes - all old tool names continue working with deprecation warnings
  - Tools removed from advertisement: `recall_memory`, `retrieve_memory`, `retrieve_with_quality_boost`, `debug_retrieve`, `exact_match_retrieve`, `recall_by_timeframe`, `delete_memory`, `delete_by_tag`, `delete_by_tags`, `delete_by_all_tags`, `delete_by_timeframe`, `delete_before_date`, `get_raw_embedding`, `consolidate_memories`, `consolidation_status`, `consolidation_recommendations`, `scheduler_status`, `trigger_consolidation`, `pause_consolidation`, `resume_consolidation`

## [10.0.1] - 2026-01-23

### Fixed
- **CRITICAL: MCP tools not loading in Claude Desktop**
  - Fixed NameError in `handle_list_tools()` caused by JavaScript-style booleans (`false`/`true`) instead of Python booleans (`False`/`True`)
  - Affected locations: `src/mcp_memory_service/server_impl.py` lines 1446, 1662, 2182, 2380
  - Impact: v10.0.0 prevented ALL MCP tools from loading in Claude Desktop
  - Error message: "name 'false' is not defined"
  - Resolution: All tool schemas now use proper Python boolean literals
  - Tools now load correctly in Claude Desktop
  - Commit: 2958aef

## [10.0.0] - 2026-01-23

### Major Changes

**MCP Tool Consolidation: 34 → 12 Tools (64% API Simplification)**

The most significant API redesign in MCP Memory Service history - consolidating 34 tools into 12 unified tools for better usability, maintainability, and MCP best practices compliance. While technically maintaining 100% backwards compatibility, this represents a new generation of the API architecture warranting a major version bump.

**Key Achievements:**
- **64% Tool Reduction**: 34 tools → 12 tools with enhanced capabilities
- **100% Backwards Compatibility**: All 33 deprecated tools continue working with deprecation warnings
- **Zero Breaking Changes**: Existing integrations work unchanged until v11.0.0
- **Enhanced Capabilities**: New unified tools offer combined functionality (e.g., filter by tags + time range)
- **Comprehensive Testing**: 62 new tests added (100% pass rate, 968 total tests)
- **Migration Guide**: Complete documentation in `docs/MIGRATION.md`

### Tool Consolidation Details

**Delete Operations (6 → 1):**
- `delete_memory`, `delete_by_tag`, `delete_by_tags`, `delete_by_all_tags`, `delete_by_timeframe`, `delete_before_date`
- **Unified as:** `memory_delete` with combined filters (tags + time range + dry_run mode)

**Search Operations (6 → 1):**
- `retrieve_memory`, `recall_memory`, `recall_by_timeframe`, `retrieve_with_quality_boost`, `exact_match_retrieve`, `debug_retrieve`
- **Unified as:** `memory_search` with modes (semantic/exact/hybrid), quality boost, and time filtering

**Consolidation Operations (7 → 1):**
- `consolidate_memories`, `consolidation_status`, `consolidation_recommendations`, `scheduler_status`, `trigger_consolidation`, `pause_consolidation`, `resume_consolidation`
- **Unified as:** `memory_consolidate` with action parameter (run/status/recommend/scheduler/pause/resume)

**Ingestion Operations (2 → 1):**
- `ingest_document`, `ingest_directory`
- **Unified as:** `memory_ingest` with automatic directory detection

**Quality Operations (3 → 1):**
- `rate_memory`, `get_memory_quality`, `analyze_quality_distribution`
- **Unified as:** `memory_quality` with action parameter (rate/get/analyze)

**Graph Operations (3 → 1):**
- `find_connected_memories`, `find_shortest_path`, `get_memory_subgraph`
- **Unified as:** `memory_graph` with action parameter (connected/path/subgraph)

**Simple Renames (5 tools):**
- `store_memory` → `memory_store`
- `check_database_health` → `memory_health`
- `get_cache_stats` → `memory_stats`
- `cleanup_duplicates` → `memory_cleanup`
- `update_memory_metadata` → `memory_update`

**New Tool:**
- `memory_list` - Browse memories with pagination (replaces `search_by_tag` with enhanced features)

### Deprecation Architecture

**New Compatibility Layer:** `src/mcp_memory_service/server/compat.py` (318 lines)
- Centralized `DEPRECATED_TOOLS` mapping with migration hints
- Automatic warning emission for deprecated tool usage
- Clean delegation to new unified handlers
- Zero performance overhead for new tools

**Deprecation Timeline:**
- **v10.0+**: All old tools work with warnings (current release)
- **v11.0**: Old tools removed (breaking change)

### Technical Improvements

**Code Quality:**
- Reduced API surface area by 64%
- Eliminated duplicate validation logic across 33 handlers
- Improved maintainability with unified error handling
- Better parameter naming consistency (e.g., `n_results` → `limit`, `content_hash` standardized)

**Testing:**
- 62 new comprehensive tests covering all tool migrations
- 100% test pass rate maintained (968 total tests)
- Validation of deprecation warnings and parameter transformations
- Integration tests for unified handler flows

**Documentation:**
- Complete migration guide with side-by-side examples
- Deprecation warnings with actionable migration hints
- Updated MCP schema with new tool definitions
- CLAUDE.md updated with new tool reference

### Migration Guide

**For Existing Users:**
1. **No Action Required**: Old tool names continue working
2. **Optional**: Update to new tool names to eliminate deprecation warnings
3. **Follow Migration Guide**: `docs/MIGRATION.md` provides mapping for all tools
4. **Timeline**: Update before v11.0.0 (removal date TBD)

**Example Migration:**
```python
# Old (v9.x) - Still works in v10.0 with warning
{"tool": "retrieve_memory", "query": "python", "n_results": 5}

# New (v10.0+) - No warnings
{"tool": "memory_search", "query": "python", "limit": 5, "mode": "semantic"}
```

**Enhanced Capabilities:**
```python
# Old (v9.x) - Required multiple tool calls
delete_by_tags(tags=["temp"])  # First call
delete_by_timeframe(start="2024-01-01")  # Second call

# New (v10.0+) - Single unified call
memory_delete(tags=["temp"], after="2024-01-01", tag_match="any")
```

### Related Issues

- Closes #372 - MCP Tool Optimization
- Related #374 - Follow-up performance optimizations

### Contributors

Special thanks to the community for feedback on API design and testing the deprecation layer during development.

---

