This is page 5 of 35. Use http://codebase.md/doobidoo/mcp-memory-service?lines=false&page={x} to view the full context.
# Directory Structure
```
├── .claude
│ ├── agents
│ │ ├── amp-bridge.md
│ │ ├── amp-pr-automator.md
│ │ ├── code-quality-guard.md
│ │ ├── gemini-pr-automator.md
│ │ └── github-release-manager.md
│ ├── settings.local.json.backup
│ └── settings.local.json.local
├── .commit-message
├── .dockerignore
├── .env.example
├── .env.sqlite.backup
├── .envnn#
├── .gitattributes
├── .github
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ ├── feature_request.yml
│ │ └── performance_issue.yml
│ ├── pull_request_template.md
│ └── workflows
│ ├── bridge-tests.yml
│ ├── CACHE_FIX.md
│ ├── claude-code-review.yml
│ ├── claude.yml
│ ├── cleanup-images.yml.disabled
│ ├── dev-setup-validation.yml
│ ├── docker-publish.yml
│ ├── LATEST_FIXES.md
│ ├── main-optimized.yml.disabled
│ ├── main.yml
│ ├── publish-and-test.yml
│ ├── README_OPTIMIZATION.md
│ ├── release-tag.yml.disabled
│ ├── release.yml
│ ├── roadmap-review-reminder.yml
│ ├── SECRET_CONDITIONAL_FIX.md
│ └── WORKFLOW_FIXES.md
├── .gitignore
├── .mcp.json.backup
├── .mcp.json.template
├── .pyscn
│ ├── .gitignore
│ └── reports
│ └── analyze_20251123_214224.html
├── AGENTS.md
├── archive
│ ├── deployment
│ │ ├── deploy_fastmcp_fixed.sh
│ │ ├── deploy_http_with_mcp.sh
│ │ └── deploy_mcp_v4.sh
│ ├── deployment-configs
│ │ ├── empty_config.yml
│ │ └── smithery.yaml
│ ├── development
│ │ └── test_fastmcp.py
│ ├── docs-removed-2025-08-23
│ │ ├── authentication.md
│ │ ├── claude_integration.md
│ │ ├── claude-code-compatibility.md
│ │ ├── claude-code-integration.md
│ │ ├── claude-code-quickstart.md
│ │ ├── claude-desktop-setup.md
│ │ ├── complete-setup-guide.md
│ │ ├── database-synchronization.md
│ │ ├── development
│ │ │ ├── autonomous-memory-consolidation.md
│ │ │ ├── CLEANUP_PLAN.md
│ │ │ ├── CLEANUP_README.md
│ │ │ ├── CLEANUP_SUMMARY.md
│ │ │ ├── dream-inspired-memory-consolidation.md
│ │ │ ├── hybrid-slm-memory-consolidation.md
│ │ │ ├── mcp-milestone.md
│ │ │ ├── multi-client-architecture.md
│ │ │ ├── test-results.md
│ │ │ └── TIMESTAMP_FIX_SUMMARY.md
│ │ ├── distributed-sync.md
│ │ ├── invocation_guide.md
│ │ ├── macos-intel.md
│ │ ├── master-guide.md
│ │ ├── mcp-client-configuration.md
│ │ ├── multi-client-server.md
│ │ ├── service-installation.md
│ │ ├── sessions
│ │ │ └── MCP_ENHANCEMENT_SESSION_MEMORY_v4.1.0.md
│ │ ├── UBUNTU_SETUP.md
│ │ ├── ubuntu.md
│ │ ├── windows-setup.md
│ │ └── windows.md
│ ├── docs-root-cleanup-2025-08-23
│ │ ├── AWESOME_LIST_SUBMISSION.md
│ │ ├── CLOUDFLARE_IMPLEMENTATION.md
│ │ ├── DOCUMENTATION_ANALYSIS.md
│ │ ├── DOCUMENTATION_CLEANUP_PLAN.md
│ │ ├── DOCUMENTATION_CONSOLIDATION_COMPLETE.md
│ │ ├── LITESTREAM_SETUP_GUIDE.md
│ │ ├── lm_studio_system_prompt.md
│ │ ├── PYTORCH_DOWNLOAD_FIX.md
│ │ └── README-ORIGINAL-BACKUP.md
│ ├── investigations
│ │ └── MACOS_HOOKS_INVESTIGATION.md
│ ├── litestream-configs-v6.3.0
│ │ ├── install_service.sh
│ │ ├── litestream_master_config_fixed.yml
│ │ ├── litestream_master_config.yml
│ │ ├── litestream_replica_config_fixed.yml
│ │ ├── litestream_replica_config.yml
│ │ ├── litestream_replica_simple.yml
│ │ ├── litestream-http.service
│ │ ├── litestream.service
│ │ └── requirements-cloudflare.txt
│ ├── release-notes
│ │ └── release-notes-v7.1.4.md
│ └── setup-development
│ ├── README.md
│ ├── setup_consolidation_mdns.sh
│ ├── STARTUP_SETUP_GUIDE.md
│ └── test_service.sh
├── CHANGELOG-HISTORIC.md
├── CHANGELOG.md
├── claude_commands
│ ├── memory-context.md
│ ├── memory-health.md
│ ├── memory-ingest-dir.md
│ ├── memory-ingest.md
│ ├── memory-recall.md
│ ├── memory-search.md
│ ├── memory-store.md
│ ├── README.md
│ └── session-start.md
├── claude-hooks
│ ├── config.json
│ ├── config.template.json
│ ├── CONFIGURATION.md
│ ├── core
│ │ ├── memory-retrieval.js
│ │ ├── mid-conversation.js
│ │ ├── session-end.js
│ │ ├── session-start.js
│ │ └── topic-change.js
│ ├── debug-pattern-test.js
│ ├── install_claude_hooks_windows.ps1
│ ├── install_hooks.py
│ ├── memory-mode-controller.js
│ ├── MIGRATION.md
│ ├── README-NATURAL-TRIGGERS.md
│ ├── README-phase2.md
│ ├── README.md
│ ├── simple-test.js
│ ├── statusline.sh
│ ├── test-adaptive-weights.js
│ ├── test-dual-protocol-hook.js
│ ├── test-mcp-hook.js
│ ├── test-natural-triggers.js
│ ├── test-recency-scoring.js
│ ├── tests
│ │ ├── integration-test.js
│ │ ├── phase2-integration-test.js
│ │ ├── test-code-execution.js
│ │ ├── test-cross-session.json
│ │ ├── test-session-tracking.json
│ │ └── test-threading.json
│ ├── utilities
│ │ ├── adaptive-pattern-detector.js
│ │ ├── context-formatter.js
│ │ ├── context-shift-detector.js
│ │ ├── conversation-analyzer.js
│ │ ├── dynamic-context-updater.js
│ │ ├── git-analyzer.js
│ │ ├── mcp-client.js
│ │ ├── memory-client.js
│ │ ├── memory-scorer.js
│ │ ├── performance-manager.js
│ │ ├── project-detector.js
│ │ ├── session-tracker.js
│ │ ├── tiered-conversation-monitor.js
│ │ └── version-checker.js
│ └── WINDOWS-SESSIONSTART-BUG.md
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Development-Sprint-November-2025.md
├── docs
│ ├── amp-cli-bridge.md
│ ├── api
│ │ ├── code-execution-interface.md
│ │ ├── memory-metadata-api.md
│ │ ├── PHASE1_IMPLEMENTATION_SUMMARY.md
│ │ ├── PHASE2_IMPLEMENTATION_SUMMARY.md
│ │ ├── PHASE2_REPORT.md
│ │ └── tag-standardization.md
│ ├── architecture
│ │ ├── search-enhancement-spec.md
│ │ └── search-examples.md
│ ├── architecture.md
│ ├── archive
│ │ └── obsolete-workflows
│ │ ├── load_memory_context.md
│ │ └── README.md
│ ├── assets
│ │ └── images
│ │ ├── dashboard-v3.3.0-preview.png
│ │ ├── memory-awareness-hooks-example.png
│ │ ├── project-infographic.svg
│ │ └── README.md
│ ├── CLAUDE_CODE_QUICK_REFERENCE.md
│ ├── cloudflare-setup.md
│ ├── deployment
│ │ ├── docker.md
│ │ ├── dual-service.md
│ │ ├── production-guide.md
│ │ └── systemd-service.md
│ ├── development
│ │ ├── ai-agent-instructions.md
│ │ ├── code-quality
│ │ │ ├── phase-2a-completion.md
│ │ │ ├── phase-2a-handle-get-prompt.md
│ │ │ ├── phase-2a-index.md
│ │ │ ├── phase-2a-install-package.md
│ │ │ └── phase-2b-session-summary.md
│ │ ├── code-quality-workflow.md
│ │ ├── dashboard-workflow.md
│ │ ├── issue-management.md
│ │ ├── pr-review-guide.md
│ │ ├── refactoring-notes.md
│ │ ├── release-checklist.md
│ │ └── todo-tracker.md
│ ├── docker-optimized-build.md
│ ├── document-ingestion.md
│ ├── DOCUMENTATION_AUDIT.md
│ ├── enhancement-roadmap-issue-14.md
│ ├── examples
│ │ ├── analysis-scripts.js
│ │ ├── maintenance-session-example.md
│ │ ├── memory-distribution-chart.jsx
│ │ └── tag-schema.json
│ ├── first-time-setup.md
│ ├── glama-deployment.md
│ ├── guides
│ │ ├── advanced-command-examples.md
│ │ ├── chromadb-migration.md
│ │ ├── commands-vs-mcp-server.md
│ │ ├── mcp-enhancements.md
│ │ ├── mdns-service-discovery.md
│ │ ├── memory-consolidation-guide.md
│ │ ├── migration.md
│ │ ├── scripts.md
│ │ └── STORAGE_BACKENDS.md
│ ├── HOOK_IMPROVEMENTS.md
│ ├── hooks
│ │ └── phase2-code-execution-migration.md
│ ├── http-server-management.md
│ ├── ide-compatability.md
│ ├── IMAGE_RETENTION_POLICY.md
│ ├── images
│ │ └── dashboard-placeholder.md
│ ├── implementation
│ │ ├── health_checks.md
│ │ └── performance.md
│ ├── IMPLEMENTATION_PLAN_HTTP_SSE.md
│ ├── integration
│ │ ├── homebrew.md
│ │ └── multi-client.md
│ ├── integrations
│ │ ├── gemini.md
│ │ ├── groq-bridge.md
│ │ ├── groq-integration-summary.md
│ │ └── groq-model-comparison.md
│ ├── integrations.md
│ ├── legacy
│ │ └── dual-protocol-hooks.md
│ ├── LM_STUDIO_COMPATIBILITY.md
│ ├── maintenance
│ │ └── memory-maintenance.md
│ ├── mastery
│ │ ├── api-reference.md
│ │ ├── architecture-overview.md
│ │ ├── configuration-guide.md
│ │ ├── local-setup-and-run.md
│ │ ├── testing-guide.md
│ │ └── troubleshooting.md
│ ├── migration
│ │ └── code-execution-api-quick-start.md
│ ├── natural-memory-triggers
│ │ ├── cli-reference.md
│ │ ├── installation-guide.md
│ │ └── performance-optimization.md
│ ├── oauth-setup.md
│ ├── pr-graphql-integration.md
│ ├── quick-setup-cloudflare-dual-environment.md
│ ├── README.md
│ ├── remote-configuration-wiki-section.md
│ ├── research
│ │ ├── code-execution-interface-implementation.md
│ │ └── code-execution-interface-summary.md
│ ├── ROADMAP.md
│ ├── sqlite-vec-backend.md
│ ├── statistics
│ │ ├── charts
│ │ │ ├── activity_patterns.png
│ │ │ ├── contributors.png
│ │ │ ├── growth_trajectory.png
│ │ │ ├── monthly_activity.png
│ │ │ └── october_sprint.png
│ │ ├── data
│ │ │ ├── activity_by_day.csv
│ │ │ ├── activity_by_hour.csv
│ │ │ ├── contributors.csv
│ │ │ └── monthly_activity.csv
│ │ ├── generate_charts.py
│ │ └── REPOSITORY_STATISTICS.md
│ ├── technical
│ │ ├── development.md
│ │ ├── memory-migration.md
│ │ ├── migration-log.md
│ │ ├── sqlite-vec-embedding-fixes.md
│ │ └── tag-storage.md
│ ├── testing
│ │ └── regression-tests.md
│ ├── testing-cloudflare-backend.md
│ ├── troubleshooting
│ │ ├── cloudflare-api-token-setup.md
│ │ ├── cloudflare-authentication.md
│ │ ├── general.md
│ │ ├── hooks-quick-reference.md
│ │ ├── pr162-schema-caching-issue.md
│ │ ├── session-end-hooks.md
│ │ └── sync-issues.md
│ └── tutorials
│ ├── advanced-techniques.md
│ ├── data-analysis.md
│ └── demo-session-walkthrough.md
├── examples
│ ├── claude_desktop_config_template.json
│ ├── claude_desktop_config_windows.json
│ ├── claude-desktop-http-config.json
│ ├── config
│ │ └── claude_desktop_config.json
│ ├── http-mcp-bridge.js
│ ├── memory_export_template.json
│ ├── README.md
│ ├── setup
│ │ └── setup_multi_client_complete.py
│ └── start_https_example.sh
├── install_service.py
├── install.py
├── LICENSE
├── NOTICE
├── pyproject.toml
├── pytest.ini
├── README.md
├── run_server.py
├── scripts
│ ├── .claude
│ │ └── settings.local.json
│ ├── archive
│ │ └── check_missing_timestamps.py
│ ├── backup
│ │ ├── backup_memories.py
│ │ ├── backup_sqlite_vec.sh
│ │ ├── export_distributable_memories.sh
│ │ └── restore_memories.py
│ ├── benchmarks
│ │ ├── benchmark_code_execution_api.py
│ │ ├── benchmark_hybrid_sync.py
│ │ └── benchmark_server_caching.py
│ ├── database
│ │ ├── analyze_sqlite_vec_db.py
│ │ ├── check_sqlite_vec_status.py
│ │ ├── db_health_check.py
│ │ └── simple_timestamp_check.py
│ ├── development
│ │ ├── debug_server_initialization.py
│ │ ├── find_orphaned_files.py
│ │ ├── fix_mdns.sh
│ │ ├── fix_sitecustomize.py
│ │ ├── remote_ingest.sh
│ │ ├── setup-git-merge-drivers.sh
│ │ ├── uv-lock-merge.sh
│ │ └── verify_hybrid_sync.py
│ ├── hooks
│ │ └── pre-commit
│ ├── installation
│ │ ├── install_linux_service.py
│ │ ├── install_macos_service.py
│ │ ├── install_uv.py
│ │ ├── install_windows_service.py
│ │ ├── install.py
│ │ ├── setup_backup_cron.sh
│ │ ├── setup_claude_mcp.sh
│ │ └── setup_cloudflare_resources.py
│ ├── linux
│ │ ├── service_status.sh
│ │ ├── start_service.sh
│ │ ├── stop_service.sh
│ │ ├── uninstall_service.sh
│ │ └── view_logs.sh
│ ├── maintenance
│ │ ├── assign_memory_types.py
│ │ ├── check_memory_types.py
│ │ ├── cleanup_corrupted_encoding.py
│ │ ├── cleanup_memories.py
│ │ ├── cleanup_organize.py
│ │ ├── consolidate_memory_types.py
│ │ ├── consolidation_mappings.json
│ │ ├── delete_orphaned_vectors_fixed.py
│ │ ├── fast_cleanup_duplicates_with_tracking.sh
│ │ ├── find_all_duplicates.py
│ │ ├── find_cloudflare_duplicates.py
│ │ ├── find_duplicates.py
│ │ ├── memory-types.md
│ │ ├── README.md
│ │ ├── recover_timestamps_from_cloudflare.py
│ │ ├── regenerate_embeddings.py
│ │ ├── repair_malformed_tags.py
│ │ ├── repair_memories.py
│ │ ├── repair_sqlite_vec_embeddings.py
│ │ ├── repair_zero_embeddings.py
│ │ ├── restore_from_json_export.py
│ │ └── scan_todos.sh
│ ├── migration
│ │ ├── cleanup_mcp_timestamps.py
│ │ ├── legacy
│ │ │ └── migrate_chroma_to_sqlite.py
│ │ ├── mcp-migration.py
│ │ ├── migrate_sqlite_vec_embeddings.py
│ │ ├── migrate_storage.py
│ │ ├── migrate_tags.py
│ │ ├── migrate_timestamps.py
│ │ ├── migrate_to_cloudflare.py
│ │ ├── migrate_to_sqlite_vec.py
│ │ ├── migrate_v5_enhanced.py
│ │ ├── TIMESTAMP_CLEANUP_README.md
│ │ └── verify_mcp_timestamps.py
│ ├── pr
│ │ ├── amp_collect_results.sh
│ │ ├── amp_detect_breaking_changes.sh
│ │ ├── amp_generate_tests.sh
│ │ ├── amp_pr_review.sh
│ │ ├── amp_quality_gate.sh
│ │ ├── amp_suggest_fixes.sh
│ │ ├── auto_review.sh
│ │ ├── detect_breaking_changes.sh
│ │ ├── generate_tests.sh
│ │ ├── lib
│ │ │ └── graphql_helpers.sh
│ │ ├── quality_gate.sh
│ │ ├── resolve_threads.sh
│ │ ├── run_pyscn_analysis.sh
│ │ ├── run_quality_checks.sh
│ │ ├── thread_status.sh
│ │ └── watch_reviews.sh
│ ├── quality
│ │ ├── fix_dead_code_install.sh
│ │ ├── phase1_dead_code_analysis.md
│ │ ├── phase2_complexity_analysis.md
│ │ ├── README_PHASE1.md
│ │ ├── README_PHASE2.md
│ │ ├── track_pyscn_metrics.sh
│ │ └── weekly_quality_review.sh
│ ├── README.md
│ ├── run
│ │ ├── run_mcp_memory.sh
│ │ ├── run-with-uv.sh
│ │ └── start_sqlite_vec.sh
│ ├── run_memory_server.py
│ ├── server
│ │ ├── check_http_server.py
│ │ ├── check_server_health.py
│ │ ├── memory_offline.py
│ │ ├── preload_models.py
│ │ ├── run_http_server.py
│ │ ├── run_memory_server.py
│ │ ├── start_http_server.bat
│ │ └── start_http_server.sh
│ ├── service
│ │ ├── deploy_dual_services.sh
│ │ ├── install_http_service.sh
│ │ ├── mcp-memory-http.service
│ │ ├── mcp-memory.service
│ │ ├── memory_service_manager.sh
│ │ ├── service_control.sh
│ │ ├── service_utils.py
│ │ └── update_service.sh
│ ├── sync
│ │ ├── check_drift.py
│ │ ├── claude_sync_commands.py
│ │ ├── export_memories.py
│ │ ├── import_memories.py
│ │ ├── litestream
│ │ │ ├── apply_local_changes.sh
│ │ │ ├── enhanced_memory_store.sh
│ │ │ ├── init_staging_db.sh
│ │ │ ├── io.litestream.replication.plist
│ │ │ ├── manual_sync.sh
│ │ │ ├── memory_sync.sh
│ │ │ ├── pull_remote_changes.sh
│ │ │ ├── push_to_remote.sh
│ │ │ ├── README.md
│ │ │ ├── resolve_conflicts.sh
│ │ │ ├── setup_local_litestream.sh
│ │ │ ├── setup_remote_litestream.sh
│ │ │ ├── staging_db_init.sql
│ │ │ ├── stash_local_changes.sh
│ │ │ ├── sync_from_remote_noconfig.sh
│ │ │ └── sync_from_remote.sh
│ │ ├── README.md
│ │ ├── safe_cloudflare_update.sh
│ │ ├── sync_memory_backends.py
│ │ └── sync_now.py
│ ├── testing
│ │ ├── run_complete_test.py
│ │ ├── run_memory_test.sh
│ │ ├── simple_test.py
│ │ ├── test_cleanup_logic.py
│ │ ├── test_cloudflare_backend.py
│ │ ├── test_docker_functionality.py
│ │ ├── test_installation.py
│ │ ├── test_mdns.py
│ │ ├── test_memory_api.py
│ │ ├── test_memory_simple.py
│ │ ├── test_migration.py
│ │ ├── test_search_api.py
│ │ ├── test_sqlite_vec_embeddings.py
│ │ ├── test_sse_events.py
│ │ ├── test-connection.py
│ │ └── test-hook.js
│ ├── utils
│ │ ├── claude_commands_utils.py
│ │ ├── generate_personalized_claude_md.sh
│ │ ├── groq
│ │ ├── groq_agent_bridge.py
│ │ ├── list-collections.py
│ │ ├── memory_wrapper_uv.py
│ │ ├── query_memories.py
│ │ ├── smithery_wrapper.py
│ │ ├── test_groq_bridge.sh
│ │ └── uv_wrapper.py
│ └── validation
│ ├── check_dev_setup.py
│ ├── check_documentation_links.py
│ ├── diagnose_backend_config.py
│ ├── validate_configuration_complete.py
│ ├── validate_memories.py
│ ├── validate_migration.py
│ ├── validate_timestamp_integrity.py
│ ├── verify_environment.py
│ ├── verify_pytorch_windows.py
│ └── verify_torch.py
├── SECURITY.md
├── selective_timestamp_recovery.py
├── SPONSORS.md
├── src
│ └── mcp_memory_service
│ ├── __init__.py
│ ├── api
│ │ ├── __init__.py
│ │ ├── client.py
│ │ ├── operations.py
│ │ ├── sync_wrapper.py
│ │ └── types.py
│ ├── backup
│ │ ├── __init__.py
│ │ └── scheduler.py
│ ├── cli
│ │ ├── __init__.py
│ │ ├── ingestion.py
│ │ ├── main.py
│ │ └── utils.py
│ ├── config.py
│ ├── consolidation
│ │ ├── __init__.py
│ │ ├── associations.py
│ │ ├── base.py
│ │ ├── clustering.py
│ │ ├── compression.py
│ │ ├── consolidator.py
│ │ ├── decay.py
│ │ ├── forgetting.py
│ │ ├── health.py
│ │ └── scheduler.py
│ ├── dependency_check.py
│ ├── discovery
│ │ ├── __init__.py
│ │ ├── client.py
│ │ └── mdns_service.py
│ ├── embeddings
│ │ ├── __init__.py
│ │ └── onnx_embeddings.py
│ ├── ingestion
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── chunker.py
│ │ ├── csv_loader.py
│ │ ├── json_loader.py
│ │ ├── pdf_loader.py
│ │ ├── registry.py
│ │ ├── semtools_loader.py
│ │ └── text_loader.py
│ ├── lm_studio_compat.py
│ ├── mcp_server.py
│ ├── models
│ │ ├── __init__.py
│ │ └── memory.py
│ ├── server.py
│ ├── services
│ │ ├── __init__.py
│ │ └── memory_service.py
│ ├── storage
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── cloudflare.py
│ │ ├── factory.py
│ │ ├── http_client.py
│ │ ├── hybrid.py
│ │ └── sqlite_vec.py
│ ├── sync
│ │ ├── __init__.py
│ │ ├── exporter.py
│ │ ├── importer.py
│ │ └── litestream_config.py
│ ├── utils
│ │ ├── __init__.py
│ │ ├── cache_manager.py
│ │ ├── content_splitter.py
│ │ ├── db_utils.py
│ │ ├── debug.py
│ │ ├── document_processing.py
│ │ ├── gpu_detection.py
│ │ ├── hashing.py
│ │ ├── http_server_manager.py
│ │ ├── port_detection.py
│ │ ├── system_detection.py
│ │ └── time_parser.py
│ └── web
│ ├── __init__.py
│ ├── api
│ │ ├── __init__.py
│ │ ├── analytics.py
│ │ ├── backup.py
│ │ ├── consolidation.py
│ │ ├── documents.py
│ │ ├── events.py
│ │ ├── health.py
│ │ ├── manage.py
│ │ ├── mcp.py
│ │ ├── memories.py
│ │ ├── search.py
│ │ └── sync.py
│ ├── app.py
│ ├── dependencies.py
│ ├── oauth
│ │ ├── __init__.py
│ │ ├── authorization.py
│ │ ├── discovery.py
│ │ ├── middleware.py
│ │ ├── models.py
│ │ ├── registration.py
│ │ └── storage.py
│ ├── sse.py
│ └── static
│ ├── app.js
│ ├── index.html
│ ├── README.md
│ ├── sse_test.html
│ └── style.css
├── start_http_debug.bat
├── start_http_server.sh
├── test_document.txt
├── test_version_checker.js
├── tests
│ ├── __init__.py
│ ├── api
│ │ ├── __init__.py
│ │ ├── test_compact_types.py
│ │ └── test_operations.py
│ ├── bridge
│ │ ├── mock_responses.js
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ └── test_http_mcp_bridge.js
│ ├── conftest.py
│ ├── consolidation
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── test_associations.py
│ │ ├── test_clustering.py
│ │ ├── test_compression.py
│ │ ├── test_consolidator.py
│ │ ├── test_decay.py
│ │ └── test_forgetting.py
│ ├── contracts
│ │ └── api-specification.yml
│ ├── integration
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── test_api_key_fallback.py
│ │ ├── test_api_memories_chronological.py
│ │ ├── test_api_tag_time_search.py
│ │ ├── test_api_with_memory_service.py
│ │ ├── test_bridge_integration.js
│ │ ├── test_cli_interfaces.py
│ │ ├── test_cloudflare_connection.py
│ │ ├── test_concurrent_clients.py
│ │ ├── test_data_serialization_consistency.py
│ │ ├── test_http_server_startup.py
│ │ ├── test_mcp_memory.py
│ │ ├── test_mdns_integration.py
│ │ ├── test_oauth_basic_auth.py
│ │ ├── test_oauth_flow.py
│ │ ├── test_server_handlers.py
│ │ └── test_store_memory.py
│ ├── performance
│ │ ├── test_background_sync.py
│ │ └── test_hybrid_live.py
│ ├── README.md
│ ├── smithery
│ │ └── test_smithery.py
│ ├── sqlite
│ │ └── simple_sqlite_vec_test.py
│ ├── test_client.py
│ ├── test_content_splitting.py
│ ├── test_database.py
│ ├── test_hybrid_cloudflare_limits.py
│ ├── test_hybrid_storage.py
│ ├── test_memory_ops.py
│ ├── test_semantic_search.py
│ ├── test_sqlite_vec_storage.py
│ ├── test_time_parser.py
│ ├── test_timestamp_preservation.py
│ ├── timestamp
│ │ ├── test_hook_vs_manual_storage.py
│ │ ├── test_issue99_final_validation.py
│ │ ├── test_search_retrieval_inconsistency.py
│ │ ├── test_timestamp_issue.py
│ │ └── test_timestamp_simple.py
│ └── unit
│ ├── conftest.py
│ ├── test_cloudflare_storage.py
│ ├── test_csv_loader.py
│ ├── test_fastapi_dependencies.py
│ ├── test_import.py
│ ├── test_json_loader.py
│ ├── test_mdns_simple.py
│ ├── test_mdns.py
│ ├── test_memory_service.py
│ ├── test_memory.py
│ ├── test_semtools_loader.py
│ ├── test_storage_interface_compatibility.py
│ └── test_tag_time_filtering.py
├── tools
│ ├── docker
│ │ ├── DEPRECATED.md
│ │ ├── docker-compose.http.yml
│ │ ├── docker-compose.pythonpath.yml
│ │ ├── docker-compose.standalone.yml
│ │ ├── docker-compose.uv.yml
│ │ ├── docker-compose.yml
│ │ ├── docker-entrypoint-persistent.sh
│ │ ├── docker-entrypoint-unified.sh
│ │ ├── docker-entrypoint.sh
│ │ ├── Dockerfile
│ │ ├── Dockerfile.glama
│ │ ├── Dockerfile.slim
│ │ ├── README.md
│ │ └── test-docker-modes.sh
│ └── README.md
└── uv.lock
```
# Files
--------------------------------------------------------------------------------
/tests/integration/test_cloudflare_connection.py:
--------------------------------------------------------------------------------
```python
#!/usr/bin/env python3
"""Integration test for Cloudflare connection and storage initialization."""
import os
import sys
import requests
from pathlib import Path
from dotenv import load_dotenv
# Add the project directory to path for standalone execution
project_dir = Path(__file__).parent.parent.parent
sys.path.insert(0, str(project_dir / "src"))
# Load environment from .env file
load_dotenv(project_dir / ".env")
def test_cloudflare_config():
"""Test Cloudflare configuration and API connectivity."""
print("=== Cloudflare Configuration Test ===")
print(f"Backend: {os.getenv('MCP_MEMORY_STORAGE_BACKEND')}")
print(f"API Token: {os.getenv('CLOUDFLARE_API_TOKEN')[:10]}..." if os.getenv('CLOUDFLARE_API_TOKEN') else "API Token: NOT SET")
print(f"Account ID: {os.getenv('CLOUDFLARE_ACCOUNT_ID')}")
print(f"D1 Database ID: {os.getenv('CLOUDFLARE_D1_DATABASE_ID')}")
print(f"Vectorize Index: {os.getenv('CLOUDFLARE_VECTORIZE_INDEX')}")
# Test Cloudflare API connection
api_token = os.getenv('CLOUDFLARE_API_TOKEN')
account_id = os.getenv('CLOUDFLARE_ACCOUNT_ID')
if api_token and account_id:
print("\n=== Testing Cloudflare API Connection ===")
headers = {
'Authorization': f'Bearer {api_token}',
'Content-Type': 'application/json'
}
# Test API token validity
url = f"https://api.cloudflare.com/client/v4/accounts/{account_id}"
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
print("✅ Cloudflare API token is valid")
return True
else:
print(f"❌ Cloudflare API error: {response.status_code}")
print(f"Response: {response.text}")
return False
except Exception as e:
print(f"❌ Connection error: {str(e)}")
return False
else:
print("❌ Missing API token or account ID")
return False
def test_storage_backend_import():
"""Test storage backend import and initialization."""
print("\n=== Testing Storage Backend Import ===")
try:
from mcp_memory_service.config import STORAGE_BACKEND
print(f"Config says backend is: {STORAGE_BACKEND}")
if STORAGE_BACKEND == 'cloudflare':
try:
from mcp_memory_service.storage.cloudflare import CloudflareStorage
print("✅ CloudflareStorage import successful")
# Try to initialize
print("\n=== Attempting to Initialize Cloudflare Storage ===")
from mcp_memory_service.config import (
CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID,
CLOUDFLARE_VECTORIZE_INDEX, CLOUDFLARE_D1_DATABASE_ID,
CLOUDFLARE_R2_BUCKET, CLOUDFLARE_EMBEDDING_MODEL,
CLOUDFLARE_LARGE_CONTENT_THRESHOLD, CLOUDFLARE_MAX_RETRIES,
CLOUDFLARE_BASE_DELAY
)
storage = CloudflareStorage(
api_token=CLOUDFLARE_API_TOKEN,
account_id=CLOUDFLARE_ACCOUNT_ID,
vectorize_index=CLOUDFLARE_VECTORIZE_INDEX,
d1_database_id=CLOUDFLARE_D1_DATABASE_ID,
r2_bucket=CLOUDFLARE_R2_BUCKET,
embedding_model=CLOUDFLARE_EMBEDDING_MODEL,
large_content_threshold=CLOUDFLARE_LARGE_CONTENT_THRESHOLD,
max_retries=CLOUDFLARE_MAX_RETRIES,
base_delay=CLOUDFLARE_BASE_DELAY
)
print("✅ CloudflareStorage initialized")
return True
except Exception as e:
print(f"❌ CloudflareMemoryStorage error: {str(e)}")
import traceback
traceback.print_exc()
return False
else:
print(f"⚠️ Backend is set to {STORAGE_BACKEND}, not cloudflare")
return False
except Exception as e:
print(f"❌ Import error: {str(e)}")
import traceback
traceback.print_exc()
return False
def main():
"""Run all Cloudflare connection tests."""
print("🧪 Running Cloudflare Integration Tests")
print("=" * 50)
success = True
# Test API connectivity
if not test_cloudflare_config():
success = False
# Test storage backend
if not test_storage_backend_import():
success = False
print("\n" + "=" * 50)
if success:
print("✅ All Cloudflare connection tests passed!")
return 0
else:
print("❌ Some Cloudflare connection tests failed!")
return 1
if __name__ == "__main__":
sys.exit(main())
```
--------------------------------------------------------------------------------
/SPONSORS.md:
--------------------------------------------------------------------------------
```markdown
# Support MCP Memory Service Development
Thank you for considering sponsoring MCP Memory Service! Your support helps maintain and enhance this production-ready knowledge management system.
## 🌟 Why Your Sponsorship Matters
MCP Memory Service is more than just a memory storage tool—it's a comprehensive knowledge management platform that:
- **Processes queries in <1 second** with advanced semantic search
- **Manages 300+ memories** in production environments
- **Provides 16 operations** for complete memory lifecycle management
- **Offers enterprise features** like automatic backups and health monitoring
- **Supports the MCP ecosystem** with a reference implementation
## 📊 Project Impact
<p align="center">
<img src="https://img.shields.io/badge/Memories_Managed-319+-blue?style=for-the-badge" />
<img src="https://img.shields.io/badge/Query_Time-828ms-green?style=for-the-badge" />
<img src="https://img.shields.io/badge/Cache_Hit_Rate-100%25-brightgreen?style=for-the-badge" />
<img src="https://img.shields.io/badge/Operations-16-orange?style=for-the-badge" />
</p>
## 💎 Sponsorship Tiers
### 🥉 Bronze Sponsor ($10/month)
- ✅ Name listed in README.md
- ✅ Access to sponsor-only discussions
- ✅ Early access to new features
- ✅ Sponsor badge on GitHub profile
### 🥈 Silver Sponsor ($50/month)
- ✅ All Bronze benefits
- ✅ Priority issue support
- ✅ Monthly development updates
- ✅ Name in release notes
- ✅ Access to development roadmap
### 🥇 Gold Sponsor ($200/month)
- ✅ All Silver benefits
- ✅ Feature request priority
- ✅ 1-on-1 monthly video call
- ✅ Logo on project documentation
- ✅ Custom integration support
### 💎 Diamond Sponsor ($500+/month)
- ✅ All Gold benefits
- ✅ Custom feature development
- ✅ Direct integration support
- ✅ Team training sessions
- ✅ Logo on project homepage
- ✅ Co-marketing opportunities
## 🎯 Sponsorship Goals
Your sponsorship directly funds:
### Immediate Goals (Q3 2025)
- [ ] **$200/month** - Automatic backup scheduling system
- [ ] **$400/month** - Multi-language support (ES, FR, DE, JP)
- [ ] **$600/month** - Cloud sync capabilities (AWS, GCP, Azure)
### Long-term Goals (Q4 2025)
- [ ] **$800/month** - Advanced analytics dashboard
- [ ] **$1000/month** - Plugin system for custom extensions
- [ ] **$1500/month** - Enterprise authentication (SSO, LDAP)
## 🤝 How to Sponsor
### GitHub Sponsors (Recommended)
<a href="https://github.com/sponsors/doobidoo">
<img src="https://img.shields.io/badge/Sponsor_on_GitHub-❤️-ea4aaa?style=for-the-badge&logo=github-sponsors" />
</a>
### One-time Donations
- **Ko-fi**: [ko-fi.com/doobidoo](https://ko-fi.com/doobidoo)
- **Buy Me a Coffee**: [buymeacoffee.com/doobidoo](https://coff.ee/doobidoo)
- **PayPal**: [paypal.me/doobidoo](https://paypal.me/heinrichkrupp1)
### Cryptocurrency
- **Bitcoin**: `bc1qypcx7m9jl3mkptvc3xrzyd7dywjctpxyvaajgr`
- **Ethereum**: `0xf049d21449D1F6FAD2B94080c40B751147F1099a`
## 🏆 Current Sponsors
### 💎 Diamond Sponsors
*Be the first Diamond sponsor!*
### 🥇 Gold Sponsors
*Be the first Gold sponsor!*
### 🥈 Silver Sponsors
*Be the first Silver sponsor!*
### 🥉 Bronze Sponsors
*Be the first Bronze sponsor!*
## 📈 Sponsorship Benefits in Detail
### For Individuals
- Support open-source development
- Get priority support for your use cases
- Shape the future of the project
- Learn from direct developer interaction
### For Companies
- Ensure long-term project sustainability
- Get custom features for your needs
- Receive integration support
- Show your commitment to open source
- Marketing visibility to our user base
## 💬 Testimonials
> "MCP Memory Service transformed how our AI assistants manage context. The semantic search is incredibly fast and accurate." - *Production User*
> "The tag system and memory maintenance features saved us hours of manual organization work." - *Enterprise User*
## 📞 Contact
For custom sponsorship packages or enterprise inquiries:
- Email: [[email protected]]
- Discord: [Join our community](https://discord.gg/cc4BU9Hqfinder)
- GitHub Discussions: [Start a conversation](https://github.com/doobidoo/mcp-memory-service/discussions)
## 🙏 Thank You
Every sponsorship, no matter the size, makes a difference. Your support enables continued development, better documentation, and a stronger community around MCP Memory Service.
Together, we're building the future of AI memory management!
---
<p align="center">
<a href="https://github.com/doobidoo/mcp-memory-service">
<img src="https://img.shields.io/github/stars/doobidoo/mcp-memory-service?style=social" />
</a>
<a href="https://github.com/doobidoo/mcp-memory-service/fork">
<img src="https://img.shields.io/github/forks/doobidoo/mcp-memory-service?style=social" />
</a>
</p>
```
--------------------------------------------------------------------------------
/docs/integrations/groq-integration-summary.md:
--------------------------------------------------------------------------------
```markdown
# Groq Bridge Integration Summary
## Overview
Groq bridge integration provides ultra-fast LLM inference (10x faster than standard models) for code quality analysis workflows. This is an **optional enhancement** to the existing Gemini CLI integration.
## Installation Status
### ✅ Completed
- Groq bridge relocated to `scripts/utils/groq_agent_bridge.py`
- Documentation moved to `docs/integrations/groq-bridge.md`
- CLAUDE.md updated with Groq integration
- code-quality-guard agent updated to support both Gemini and Groq
- Pre-commit hook installed at `.git/hooks/pre-commit`
### ⚠️ Pending (User Setup Required)
1. Install groq Python package:
```bash
pip install groq
# or
uv pip install groq
```
2. Set GROQ_API_KEY:
```bash
export GROQ_API_KEY="your-api-key-here"
# Get your key from: https://console.groq.com/keys
```
## Usage
### Gemini CLI (Default - Currently Working)
```bash
# Complexity analysis
gemini "Analyze complexity 1-10 per function: $(cat file.py)"
# Security scan
gemini "Check for security vulnerabilities: $(cat file.py)"
```
### Groq Bridge (Optional - After Setup)
```bash
# Complexity analysis (10x faster)
python scripts/utils/groq_agent_bridge.py "Analyze complexity 1-10 per function: $(cat file.py)"
# Security scan (10x faster)
python scripts/utils/groq_agent_bridge.py "Check for security vulnerabilities: $(cat file.py)" --json
# With custom model
python scripts/utils/groq_agent_bridge.py "Your prompt" --model llama2-70b-4096 --temperature 0.3
```
## Pre-Commit Hook
The pre-commit hook is installed and will run automatically on `git commit`. It currently uses **Gemini CLI** by default.
**What it checks:**
- Code complexity (blocks if score >8, warns if score 7)
- Security vulnerabilities (blocks on any findings)
- SQL injection, XSS, command injection patterns
- Hardcoded secrets
**Hook location:** `.git/hooks/pre-commit` → `scripts/hooks/pre-commit`
**To use Groq instead of Gemini in hooks:**
Edit `scripts/hooks/pre-commit` and replace `gemini` commands with:
```bash
python scripts/utils/groq_agent_bridge.py
```
## Testing the Integration
### Test Groq Bridge (Requires Setup)
```bash
# Quick test
bash scripts/utils/test_groq_bridge.sh
# Manual test
python scripts/utils/groq_agent_bridge.py "Rate the complexity of: def add(a,b): return a+b"
```
### Test Pre-Commit Hook (Uses Gemini)
```bash
# Create a test file
echo "def test(): pass" > test.py
# Stage it
git add test.py
# Commit will trigger hook
git commit -m "test: pre-commit hook"
# The hook will run Gemini CLI analysis automatically
```
## Performance Comparison
| Task | Gemini CLI | Groq Bridge | Speedup |
|------|-----------|-------------|---------|
| Complexity analysis (1 file) | ~3-5s | ~300-500ms | 10x |
| Security scan (1 file) | ~3-5s | ~300-500ms | 10x |
| TODO prioritization (10 files) | ~30s | ~3s | 10x |
## When to Use Each
**Use Gemini CLI (default):**
- ✅ Already authenticated and working
- ✅ One-off analysis during development
- ✅ No setup required
**Use Groq Bridge (optional):**
- ✅ CI/CD pipelines (faster builds)
- ✅ Large-scale codebase analysis
- ✅ Pre-commit hooks on large files
- ✅ Batch processing multiple files
## Integration Points
The Groq bridge is integrated into:
1. **code-quality-guard agent** (`.claude/agents/code-quality-guard.md`)
- Supports both Gemini and Groq
- Examples show both options
2. **CLAUDE.md** (lines 343-377)
- Agent integrations table updated
- Usage examples for both tools
3. **Pre-commit hook** (`scripts/hooks/pre-commit`)
- Currently uses Gemini (working out of the box)
- Can be switched to Groq after setup
4. **Utility scripts** (`scripts/utils/`)
- `groq_agent_bridge.py` - Main bridge implementation
- `test_groq_bridge.sh` - Integration test script
## Troubleshooting
**Issue: ModuleNotFoundError: No module named 'groq'**
```bash
pip install groq
# or
uv pip install groq
```
**Issue: GROQ_API_KEY environment variable required**
```bash
export GROQ_API_KEY="your-api-key"
# Get key from: https://console.groq.com/keys
```
**Issue: Gemini CLI authentication in pre-commit hook**
- The hook uses the Gemini CLI from your PATH
- Authentication state should be shared across terminal sessions
- If issues persist, manually run: `gemini --version` to authenticate
## Next Steps
1. **Optional**: Install groq package and set API key to enable ultra-fast inference
2. **Test**: Run a manual commit to see pre-commit hook in action with Gemini
3. **Optimize**: Switch pre-commit hook to Groq for faster CI/CD workflows
## Documentation References
- Groq Bridge Setup: `docs/integrations/groq-bridge.md`
- Code Quality Agent: `.claude/agents/code-quality-guard.md`
- CLAUDE.md Agent Section: Lines 343-377
```
--------------------------------------------------------------------------------
/src/mcp_memory_service/utils/debug.py:
--------------------------------------------------------------------------------
```python
# Copyright 2024 Heinrich Krupp
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Debug utilities for memory service."""
from typing import Dict, Any, List
import numpy as np
from ..models.memory import Memory, MemoryQueryResult
def _get_embedding_model(storage):
"""
Get the embedding model from storage, handling different backend attribute names.
Different backends use different attribute names (e.g., 'model', 'embedding_model').
"""
if hasattr(storage, 'model') and storage.model is not None:
return storage.model
elif hasattr(storage, 'embedding_model') and storage.embedding_model is not None:
return storage.embedding_model
else:
raise AttributeError(f"Storage backend {type(storage).__name__} has no embedding model attribute")
def get_raw_embedding(storage, content: str) -> Dict[str, Any]:
"""Get raw embedding vector for content."""
try:
model = _get_embedding_model(storage)
embedding = model.encode(content).tolist()
return {
"status": "success",
"embedding": embedding,
"dimension": len(embedding)
}
except Exception as e:
return {
"status": "error",
"error": str(e)
}
def check_embedding_model(storage) -> Dict[str, Any]:
"""Check if embedding model is loaded and working."""
try:
model = _get_embedding_model(storage)
test_embedding = model.encode("test").tolist()
# Try to get model name, handling different model types
model_name = "unknown"
if hasattr(model, '_model_card_vars'):
model_name = model._model_card_vars.get('modelname', 'unknown')
elif hasattr(storage, 'embedding_model_name'):
model_name = storage.embedding_model_name
return {
"status": "healthy",
"model_loaded": True,
"model_name": model_name,
"embedding_dimension": len(test_embedding)
}
except Exception as e:
return {
"status": "unhealthy",
"error": str(e)
}
async def debug_retrieve_memory(
storage,
query: str,
n_results: int = 5,
similarity_threshold: float = 0.0
) -> List[MemoryQueryResult]:
"""Retrieve memories with enhanced debug information including raw similarity scores."""
try:
# Use the storage's retrieve method which handles embedding generation and search
results = await storage.retrieve(query, n_results)
# Filter by similarity threshold and enhance debug info
filtered_results = []
for result in results:
if result.relevance_score >= similarity_threshold:
# Enhance debug info with additional details
enhanced_debug_info = result.debug_info.copy() if result.debug_info else {}
enhanced_debug_info.update({
"raw_similarity": result.relevance_score,
"backend": type(storage).__name__,
"query": query,
"similarity_threshold": similarity_threshold
})
filtered_results.append(
MemoryQueryResult(
memory=result.memory,
relevance_score=result.relevance_score,
debug_info=enhanced_debug_info
)
)
return filtered_results
except Exception as e:
# Return empty list on error to match original behavior
return []
async def exact_match_retrieve(storage, content: str) -> List[Memory]:
"""Retrieve memories using exact content match."""
try:
results = storage.collection.get(
where={"content": content}
)
memories = []
for i in range(len(results["ids"])):
memory = Memory.from_dict(
{
"content": results["documents"][i],
**results["metadatas"][i]
},
embedding=results["embeddings"][i] if "embeddings" in results else None
)
memories.append(memory)
return memories
except Exception as e:
return []
```
--------------------------------------------------------------------------------
/.github/workflows/bridge-tests.yml:
--------------------------------------------------------------------------------
```yaml
name: HTTP-MCP Bridge Tests
on:
push:
paths:
- 'examples/http-mcp-bridge.js'
- 'tests/bridge/**'
- 'tests/integration/test_bridge_integration.js'
- '.github/workflows/bridge-tests.yml'
pull_request:
paths:
- 'examples/http-mcp-bridge.js'
- 'tests/bridge/**'
- 'tests/integration/test_bridge_integration.js'
jobs:
test-bridge:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install bridge unit test dependencies
run: npm install
working-directory: tests/bridge
- name: Install integration test dependencies
run: npm install
working-directory: tests/integration
- name: Run bridge unit tests
run: npm test
working-directory: tests/bridge
- name: Run integration tests
run: npm test
working-directory: tests/integration
- name: Validate health endpoint fix
run: |
if ! grep -q "/api/health" examples/http-mcp-bridge.js; then
echo "❌ ERROR: Health endpoint should be /api/health"
exit 1
fi
echo "✅ Health endpoint check passed"
- name: Validate status code handling fix
run: |
if grep -v "|| response.statusCode === 201" examples/http-mcp-bridge.js | grep -q "statusCode === 201"; then
echo "❌ ERROR: Found hardcoded 201-only status check - server returns 200!"
exit 1
fi
echo "✅ Status code handling check passed"
- name: Validate URL construction fix
run: |
if grep -q "new URL(path, this.endpoint)" examples/http-mcp-bridge.js; then
echo "❌ ERROR: Old URL construction bug still present"
exit 1
fi
echo "✅ URL construction check passed"
contract-validation:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
- name: Validate API contracts
run: node -e "
const path = require('path');
const mocks = require('./tests/bridge/mock_responses.js');
const memorySuccess = mocks.mockResponses.memories.createSuccess;
if (memorySuccess.status !== 200) throw new Error('Memory creation should return 200');
if (!memorySuccess.body.hasOwnProperty('success')) throw new Error('Memory response must have success field');
console.log('✅ API contract validation passed');
"
- name: Generate contract report
run: |
echo "# API Contract Report" > contract-report.md
echo "Generated: $(date)" >> contract-report.md
echo "" >> contract-report.md
echo "## Critical Endpoints Validated" >> contract-report.md
echo "- ✅ POST /api/memories: Returns 200 with success field" >> contract-report.md
echo "- ✅ GET /api/health: Correct endpoint path used" >> contract-report.md
echo "- ✅ URL construction: Preserves /api base path" >> contract-report.md
echo "- ✅ Status codes: Handles HTTP 200 correctly" >> contract-report.md
- name: Upload contract report
uses: actions/upload-artifact@v4
if: always()
with:
name: api-contract-report
path: contract-report.md
quick-smoke-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
- name: Check JavaScript syntax
run: node -c examples/http-mcp-bridge.js
- name: Test bridge instantiation
run: node -e "
const Bridge = require('./examples/http-mcp-bridge.js');
const bridge = new Bridge();
console.log('✅ Bridge can be instantiated');
"
- name: Test URL construction
run: |
node -e "
const Bridge = require('./examples/http-mcp-bridge.js');
const bridge = new Bridge();
bridge.endpoint = 'https://test.local:8443/api';
const fullPath = '/memories';
let baseUrl;
if (bridge.endpoint.endsWith('/')) {
baseUrl = bridge.endpoint.slice(0, -1);
} else {
baseUrl = bridge.endpoint;
}
if (baseUrl + fullPath !== 'https://test.local:8443/api/memories') {
throw new Error('URL construction test failed');
}
console.log('✅ URL construction works correctly');
"
```
--------------------------------------------------------------------------------
/claude-hooks/tests/test-threading.json:
--------------------------------------------------------------------------------
```json
{
"sessions": [
{
"id": "session-1",
"startTime": "2025-10-03T13:00:20.984Z",
"endTime": "2025-10-03T13:00:20.985Z",
"projectContext": {
"name": "mcp-memory-service",
"type": "Multi-language Project",
"languages": [
"javascript",
"python"
],
"frameworks": [
"node.js",
"fastapi"
],
"tools": [
"git",
"npm",
"pip"
],
"confidence": 0.95
},
"workingDirectory": "/test/directory",
"initialTopics": [],
"finalTopics": [],
"memoriesLoaded": [],
"memoriesCreated": [],
"conversationSummary": "Implemented auth",
"outcome": {
"type": "completed",
"summary": "Implemented auth"
},
"threadId": "thread-46e4b32e1e1d1fe9",
"parentSessionId": null,
"childSessionIds": [
"session-2"
],
"status": "completed"
},
{
"id": "session-2",
"startTime": "2025-10-03T13:00:20.985Z",
"endTime": null,
"projectContext": {
"name": "mcp-memory-service",
"type": "Multi-language Project",
"languages": [
"javascript",
"python"
],
"frameworks": [
"node.js",
"fastapi"
],
"tools": [
"git",
"npm",
"pip"
],
"confidence": 0.95
},
"workingDirectory": "/test/directory",
"initialTopics": [],
"finalTopics": [],
"memoriesLoaded": [],
"memoriesCreated": [],
"conversationSummary": null,
"outcome": null,
"threadId": "thread-46e4b32e1e1d1fe9",
"parentSessionId": "session-1",
"childSessionIds": [],
"status": "active"
}
],
"conversationThreads": [
{
"id": "thread-3c54682968af57cc",
"createdAt": "2025-08-20T11:38:33.002Z",
"projectContext": {
"name": "mcp-memory-service",
"type": "Multi-language Project",
"languages": [
"javascript",
"python"
],
"frameworks": [
"node.js",
"fastapi"
],
"tools": [
"git",
"npm",
"pip"
],
"confidence": 0.95
},
"sessionIds": [
"session-1"
],
"topics": [],
"outcomes": [
{
"sessionId": "session-1",
"outcome": {
"type": "completed",
"summary": "Implemented auth"
},
"timestamp": "2025-08-20T11:38:33.003Z"
},
{
"sessionId": "session-1",
"outcome": {
"type": "completed",
"summary": "Implemented auth"
},
"timestamp": "2025-08-20T11:42:10.898Z"
},
{
"sessionId": "session-1",
"outcome": {
"type": "completed",
"summary": "Implemented auth"
},
"timestamp": "2025-08-20T11:43:24.009Z"
},
{
"sessionId": "session-1",
"outcome": {
"type": "completed",
"summary": "Implemented auth"
},
"timestamp": "2025-08-20T11:43:49.798Z"
},
{
"sessionId": "session-1",
"outcome": {
"type": "completed",
"summary": "Implemented auth"
},
"timestamp": "2025-08-20T11:44:35.339Z"
},
{
"sessionId": "session-1",
"outcome": {
"type": "completed",
"summary": "Implemented auth"
},
"timestamp": "2025-08-20T11:45:38.591Z"
}
],
"status": "active",
"lastUpdated": "2025-08-20T11:45:38.591Z"
},
{
"id": "thread-46e4b32e1e1d1fe9",
"createdAt": "2025-10-03T13:00:20.984Z",
"projectContext": {
"name": "mcp-memory-service",
"type": "Multi-language Project",
"languages": [
"javascript",
"python"
],
"frameworks": [
"node.js",
"fastapi"
],
"tools": [
"git",
"npm",
"pip"
],
"confidence": 0.95
},
"sessionIds": [
"session-1"
],
"topics": [],
"outcomes": [
{
"sessionId": "session-1",
"outcome": {
"type": "completed",
"summary": "Implemented auth"
},
"timestamp": "2025-10-03T13:00:20.985Z"
}
],
"status": "active",
"lastUpdated": "2025-10-03T13:00:20.985Z"
}
],
"projectSessions": {
"mcp-memory-service": [
"session-1",
"session-2"
]
},
"lastSaved": "2025-10-03T13:00:20.985Z"
}
```
--------------------------------------------------------------------------------
/claude-hooks/config.json:
--------------------------------------------------------------------------------
```json
{
"codeExecution": {
"enabled": true,
"timeout": 15000,
"fallbackToMCP": true,
"enableMetrics": true,
"pythonPath": "python3"
},
"memoryService": {
"protocol": "auto",
"preferredProtocol": "http",
"fallbackEnabled": true,
"http": {
"endpoint": "http://127.0.0.1:8000",
"apiKey": "VhOGAoUOE5_BMzu-phDORdyXHNMcDRBxvndK_Uop",
"healthCheckTimeout": 3000,
"useDetailedHealthCheck": true
},
"mcp": {
"serverCommand": [
"uv",
"run",
"memory",
"server",
"-s",
"hybrid"
],
"serverWorkingDir": "/home/hkr/repositories/mcp-memory-service",
"connectionTimeout": 2000,
"toolCallTimeout": 3000
},
"defaultTags": [
"claude-code",
"auto-generated"
],
"maxMemoriesPerSession": 8,
"enableSessionConsolidation": true,
"injectAfterCompacting": false,
"recentFirstMode": true,
"recentMemoryRatio": 0.6,
"recentTimeWindow": "last week",
"fallbackTimeWindow": "last 2 weeks",
"showStorageSource": true,
"sourceDisplayMode": "brief"
},
"projectDetection": {
"gitRepository": true,
"packageFiles": [
"package.json",
"pyproject.toml",
"Cargo.toml",
"go.mod",
"pom.xml"
],
"frameworkDetection": true,
"languageDetection": true,
"confidenceThreshold": 0.3
},
"memoryScoring": {
"weights": {
"timeDecay": 0.5,
"tagRelevance": 0.2,
"contentRelevance": 0.15,
"contentQuality": 0.2,
"conversationRelevance": 0.25
},
"minRelevanceScore": 0.4,
"timeDecayRate": 0.15,
"enableConversationContext": true,
"autoCalibrate": true
},
"contextFormatting": {
"includeProjectSummary": true,
"includeRelevanceScores": false,
"groupByCategory": true,
"maxContentLength": 500,
"maxContentLengthCLI": 400,
"maxContentLengthCategorized": 350,
"includeTimestamps": true,
"truncationMode": "smart"
},
"sessionAnalysis": {
"extractTopics": true,
"extractDecisions": true,
"extractInsights": true,
"extractCodeChanges": true,
"extractNextSteps": true,
"minSessionLength": 100,
"minConfidence": 0.1
},
"hooks": {
"sessionStart": {
"enabled": true,
"timeout": 20000,
"priority": "high"
},
"midConversation": {
"timeout": 8000,
"priority": "high"
},
"sessionEnd": {
"enabled": true,
"timeout": 15000,
"priority": "normal"
},
"topicChange": {
"enabled": false,
"timeout": 5000,
"priority": "low"
}
},
"gitAnalysis": {
"enabled": true,
"commitLookback": 14,
"maxCommits": 20,
"includeChangelog": true,
"maxGitMemories": 3,
"gitContextWeight": 1.2
},
"output": {
"verbose": true,
"showMemoryDetails": true,
"showProjectDetails": true,
"showScoringDetails": false,
"showRecencyInfo": true,
"showPhaseDetails": true,
"showGitAnalysis": true,
"cleanMode": false,
"style": "balanced",
"showScoringBreakdown": false,
"adaptiveTruncation": true,
"prioritySections": [
"recent-work",
"current-problems",
"key-decisions",
"additional-context"
]
},
"contentLength": {
"manyMemories": 300,
"fewMemories": 500,
"veryFewMemories": 800
},
"naturalTriggers": {
"enabled": true,
"triggerThreshold": 0.6,
"cooldownPeriod": 30000,
"maxMemoriesPerTrigger": 5
},
"performance": {
"defaultProfile": "balanced",
"enableMonitoring": true,
"autoAdjust": true,
"profiles": {
"speed_focused": {
"maxLatency": 100,
"enabledTiers": [
"instant"
],
"backgroundProcessing": false,
"degradeThreshold": 200,
"description": "Fastest response, minimal memory awareness"
},
"balanced": {
"maxLatency": 200,
"enabledTiers": [
"instant",
"fast"
],
"backgroundProcessing": true,
"degradeThreshold": 400,
"description": "Moderate latency, smart memory triggers"
},
"memory_aware": {
"maxLatency": 500,
"enabledTiers": [
"instant",
"fast",
"intensive"
],
"backgroundProcessing": true,
"degradeThreshold": 1000,
"description": "Full memory awareness, accept higher latency"
}
}
},
"conversationMonitor": {
"contextWindow": 10,
"enableCaching": true,
"maxCacheSize": 100
},
"patternDetector": {
"sensitivity": 0.7,
"adaptiveLearning": true,
"learningRate": 0.05
},
"logging": {
"level": "debug",
"enableDebug": true,
"logToFile": true,
"logFilePath": "./claude-hooks.log"
}
}
```
--------------------------------------------------------------------------------
/claude_commands/memory-health.md:
--------------------------------------------------------------------------------
```markdown
# Check Memory Service Health
I'll help you check the health and status of your MCP Memory Service, providing detailed diagnostics and statistics about your memory storage and service connectivity.
## What I'll do:
1. **Service Discovery**: I'll locate your MCP Memory Service using mDNS auto-discovery or configured endpoints.
2. **Connectivity Test**: I'll verify that the service is running and accessible from Claude Code.
3. **Health Assessment**: I'll check the database health, memory statistics, and service performance.
4. **Storage Analysis**: I'll provide insights about your stored memories, database size, and usage patterns.
5. **Troubleshooting**: If issues are found, I'll provide specific recommendations for resolution.
## Usage Examples:
```bash
claude /memory-health
claude /memory-health --detailed
claude /memory-health --test-operations
claude /memory-health --export-report
```
## Implementation:
I'll perform comprehensive health checks using your MCP Memory Service at `https://memory.local:8443/`:
1. **Service Detection**:
- Connect to configured HTTPS endpoint
- Check health endpoint at `/api/health/detailed`
- Verify HTTPS connectivity with `-k` flag for self-signed certificates
2. **Basic Health Check**:
- Service responsiveness via `/api/health` endpoint
- Detailed diagnostics via `/api/health/detailed`
- Database connection status and embedding model availability
3. **Detailed Diagnostics**:
- Memory count and database statistics from API response
- Storage backend type (SQLite-vec, ChromaDB, etc.)
- Performance metrics, uptime, and response times
- Disk usage warnings and system resource status
4. **Operational Testing** (if requested):
- Test memory storage and retrieval
- Verify search functionality
- Check tag operations
## Health Report Includes:
### Service Status
- **Running**: Whether the MCP Memory Service is active
- **Endpoint**: Service URL and port information
- **Response Time**: Average API response latency
- **Version**: Service version and backend type
- **Uptime**: How long the service has been running
### Database Health
- **Backend Type**: ChromaDB, SQLite-vec, or other storage backend
- **Connection Status**: Database connectivity and integrity
- **Total Memories**: Number of stored memory entries
- **Database Size**: Storage space used by the database
- **Last Backup**: When backups were last created (if applicable)
### Performance Metrics
- **Query Performance**: Average search and retrieval times
- **Embedding Model**: Model type and loading status
- **Memory Usage**: Service memory consumption
- **Cache Status**: Embedding and model cache statistics
### Storage Statistics
- **Popular Tags**: Most frequently used tags
- **Memory Types**: Distribution of memory types (note, decision, etc.)
- **Recent Activity**: Recent storage and retrieval operations
- **Growth Trends**: Database growth over time
## Troubleshooting Features:
### Common Issues I'll Check:
- **Service Not Running**: Instructions to start the MCP Memory Service
- **Port Conflicts**: Detection of port usage conflicts
- **Database Corruption**: Database integrity verification
- **Permission Issues**: File system access and write permissions
- **Model Loading**: Embedding model download and loading status
### Auto-Fix Capabilities:
- **Restart Service**: Attempt to restart a hung service
- **Clear Cache**: Clean corrupted cache files
- **Repair Database**: Basic database repair operations
- **Update Configuration**: Fix common configuration issues
## Arguments:
- `$ARGUMENTS` - Optional specific health check focus
- `--detailed` - Show comprehensive diagnostics and statistics
- `--test-operations` - Perform functional tests of store/retrieve operations
- `--check-backups` - Verify backup system health and recent backups
- `--performance-test` - Run performance benchmarks
- `--export-report` - Save health report to file for sharing
- `--fix-issues` - Attempt automatic fixes for detected problems
- `--quiet` - Show only critical issues (minimal output)
## Advanced Diagnostics:
- **Network Connectivity**: Test mDNS discovery and HTTP endpoints
- **Database Integrity**: Verify database consistency and repair if needed
- **Model Health**: Check embedding model files and performance
- **Configuration Validation**: Verify environment variables and settings
- **Resource Usage**: Monitor CPU, memory, and disk usage
If critical issues are detected, I'll provide step-by-step resolution instructions and can attempt automatic fixes with your permission. For complex issues, I'll recommend specific diagnostic commands or log file locations to investigate further.
The health check helps ensure your memory service is running optimally and can identify potential issues before they impact your workflow.
```
--------------------------------------------------------------------------------
/selective_timestamp_recovery.py:
--------------------------------------------------------------------------------
```python
#!/usr/bin/env python3
"""
Selective Timestamp Recovery Script
Merges backup timestamps with current database:
- Restores timestamps for 2,174 corrupted memories from Nov 5 backup
- Preserves 807 new memories created Nov 5-17 with their current timestamps
- Result: ALL 2,981 memories have correct timestamps!
"""
import sqlite3
import sys
from datetime import datetime
DRY_RUN = '--apply' not in sys.argv
current_db = r'C:\Users\heinrich.krupp\AppData\Local\mcp-memory\backups\sqlite_vec.db'
backup_db = r'C:\Users\heinrich.krupp\AppData\Local\mcp-memory\backups\sqlite_vec.backup-20251105-114637.db'
def selective_recovery():
print("="*80)
print("SELECTIVE TIMESTAMP RECOVERY")
print("="*80)
print(f"Mode: {'DRY RUN (no changes)' if not DRY_RUN else 'LIVE (applying changes)'}")
print()
# Open databases
current = sqlite3.connect(current_db)
backup = sqlite3.connect(backup_db)
# Get common hashes
cur_hashes = {h[0] for h in current.execute('SELECT content_hash FROM memories').fetchall()}
bak_hashes = {h[0] for h in backup.execute('SELECT content_hash FROM memories').fetchall()}
common = cur_hashes & bak_hashes
only_current = cur_hashes - bak_hashes
print(f"Analysis:")
print(f" - {len(common):4} memories to restore from backup")
print(f" - {len(only_current):4} new memories to keep as-is")
print(f" - {len(common) + len(only_current):4} total memories after recovery")
print()
if DRY_RUN:
print("[DRY RUN] Pass --apply to make actual changes")
print()
# Restore timestamps for common memories
restored_count = 0
errors = 0
print("Restoring timestamps...")
for i, content_hash in enumerate(common, 1):
try:
# Get timestamps from backup
bak_row = backup.execute('''
SELECT created_at, created_at_iso, updated_at, updated_at_iso
FROM memories WHERE content_hash = ?
''', (content_hash,)).fetchone()
if not bak_row:
continue
created_at, created_at_iso, updated_at, updated_at_iso = bak_row
# Check if actually corrupted (created on Nov 17)
cur_created_iso = current.execute(
'SELECT created_at_iso FROM memories WHERE content_hash = ?',
(content_hash,)
).fetchone()[0]
if '2025-11-17' in cur_created_iso:
# Corrupted - restore from backup
if not DRY_RUN:
current.execute('''
UPDATE memories
SET created_at = ?, created_at_iso = ?,
updated_at = ?, updated_at_iso = ?
WHERE content_hash = ?
''', (created_at, created_at_iso, updated_at, updated_at_iso, content_hash))
restored_count += 1
if restored_count <= 10: # Show first 10
print(f" {restored_count:4}. {content_hash[:8]}: {cur_created_iso} => {created_at_iso}")
if i % 100 == 0:
print(f" Progress: {i}/{len(common)} ({i*100//len(common)}%)")
except Exception as e:
errors += 1
if errors <= 5:
print(f" ERROR: {content_hash[:8]}: {e}")
if restored_count > 10:
print(f" ... and {restored_count - 10} more")
# Commit changes
if not DRY_RUN:
current.commit()
print(f"\n[SUCCESS] Committed {restored_count} timestamp restorations")
else:
print(f"\n[DRY RUN] Would restore {restored_count} timestamps")
print()
print("="*80)
print("RECOVERY COMPLETE!")
print("="*80)
print(f" Restored: {restored_count:4} memories from backup")
print(f" Preserved: {len(only_current):4} new memories (Nov 5-17)")
print(f" Errors: {errors:4}")
print()
if restored_count > 0:
print("Verification:")
cur = current.execute('SELECT COUNT(*) FROM memories WHERE created_at_iso LIKE "2025-11-17%"')
remaining_corrupt = cur.fetchone()[0]
print(f" Memories still with Nov 17 timestamp: {remaining_corrupt}")
print(f" (Should be ~{len(only_current)} - the genuinely new ones)")
current.close()
backup.close()
if DRY_RUN:
print()
print("[DRY RUN] No changes were made.")
print(" Run with --apply to actually fix the database:")
print(f" python {sys.argv[0]} --apply")
else:
print()
print("[SUCCESS] Database has been updated!")
print(" Restart HTTP server to use the fixed database.")
if __name__ == '__main__':
try:
selective_recovery()
except Exception as e:
print(f"\n[ERROR] {e}")
import traceback
traceback.print_exc()
sys.exit(1)
```
--------------------------------------------------------------------------------
/claude-hooks/tests/test-cross-session.json:
--------------------------------------------------------------------------------
```json
{
"sessions": [
{
"id": "cross-session-1",
"startTime": "2025-10-03T13:00:20.986Z",
"endTime": "2025-10-03T13:00:20.986Z",
"projectContext": {
"name": "mcp-memory-service",
"type": "Multi-language Project",
"languages": [
"javascript",
"python"
],
"frameworks": [
"node.js",
"fastapi"
],
"tools": [
"git",
"npm",
"pip"
],
"confidence": 0.95
},
"initialTopics": [],
"finalTopics": [
"auth",
"jwt"
],
"memoriesLoaded": [],
"memoriesCreated": [],
"conversationSummary": "Implemented user authentication",
"outcome": {
"type": "implementation",
"summary": "Implemented user authentication",
"topics": [
"auth",
"jwt"
]
},
"threadId": "thread-ee93501cb0e678ad",
"parentSessionId": null,
"childSessionIds": [],
"status": "completed"
}
],
"conversationThreads": [
{
"id": "thread-93a8463caef492e9",
"createdAt": "2025-08-20T11:38:33.004Z",
"projectContext": {
"name": "mcp-memory-service",
"type": "Multi-language Project",
"languages": [
"javascript",
"python"
],
"frameworks": [
"node.js",
"fastapi"
],
"tools": [
"git",
"npm",
"pip"
],
"confidence": 0.95
},
"sessionIds": [
"cross-session-1"
],
"topics": [
"auth",
"jwt"
],
"outcomes": [
{
"sessionId": "cross-session-1",
"outcome": {
"type": "implementation",
"summary": "Implemented user authentication",
"topics": [
"auth",
"jwt"
]
},
"timestamp": "2025-08-20T11:38:33.004Z"
},
{
"sessionId": "cross-session-1",
"outcome": {
"type": "implementation",
"summary": "Implemented user authentication",
"topics": [
"auth",
"jwt"
]
},
"timestamp": "2025-08-20T11:42:10.899Z"
},
{
"sessionId": "cross-session-1",
"outcome": {
"type": "implementation",
"summary": "Implemented user authentication",
"topics": [
"auth",
"jwt"
]
},
"timestamp": "2025-08-20T11:43:24.011Z"
},
{
"sessionId": "cross-session-1",
"outcome": {
"type": "implementation",
"summary": "Implemented user authentication",
"topics": [
"auth",
"jwt"
]
},
"timestamp": "2025-08-20T11:43:49.800Z"
},
{
"sessionId": "cross-session-1",
"outcome": {
"type": "implementation",
"summary": "Implemented user authentication",
"topics": [
"auth",
"jwt"
]
},
"timestamp": "2025-08-20T11:44:35.340Z"
},
{
"sessionId": "cross-session-1",
"outcome": {
"type": "implementation",
"summary": "Implemented user authentication",
"topics": [
"auth",
"jwt"
]
},
"timestamp": "2025-08-20T11:45:38.592Z"
}
],
"status": "active",
"lastUpdated": "2025-08-20T11:45:38.592Z"
},
{
"id": "thread-ee93501cb0e678ad",
"createdAt": "2025-10-03T13:00:20.986Z",
"projectContext": {
"name": "mcp-memory-service",
"type": "Multi-language Project",
"languages": [
"javascript",
"python"
],
"frameworks": [
"node.js",
"fastapi"
],
"tools": [
"git",
"npm",
"pip"
],
"confidence": 0.95
},
"sessionIds": [
"cross-session-1"
],
"topics": [
"auth",
"jwt"
],
"outcomes": [
{
"sessionId": "cross-session-1",
"outcome": {
"type": "implementation",
"summary": "Implemented user authentication",
"topics": [
"auth",
"jwt"
]
},
"timestamp": "2025-10-03T13:00:20.986Z"
}
],
"status": "active",
"lastUpdated": "2025-10-03T13:00:20.986Z"
}
],
"projectSessions": {
"mcp-memory-service": [
"cross-session-1"
]
},
"lastSaved": "2025-10-03T13:00:20.986Z"
}
```
--------------------------------------------------------------------------------
/docs/mastery/configuration-guide.md:
--------------------------------------------------------------------------------
```markdown
# MCP Memory Service — Configuration Guide
All configuration is driven via environment variables and sensible defaults resolved in `src/mcp_memory_service/config.py`.
## Base Paths
- `MCP_MEMORY_BASE_DIR`: Root directory for service data. Defaults per-OS to an app-data directory and is created if missing.
- Derived paths (auto-created):
- Chroma path: `${BASE_DIR}/chroma_db` unless overridden.
- Backups path: `${BASE_DIR}/backups` unless overridden.
Overrides:
- `MCP_MEMORY_CHROMA_PATH` or `mcpMemoryChromaPath`: ChromaDB directory path.
- `MCP_MEMORY_BACKUPS_PATH` or `mcpMemoryBackupsPath`: Backups directory path.
## Storage Backend Selection
- `MCP_MEMORY_STORAGE_BACKEND`: `sqlite_vec` (default), `chroma`, or `cloudflare`.
- `sqlite-vec` aliases to `sqlite_vec`.
- Unknown values default to `sqlite_vec` with a warning.
SQLite-vec options:
- `MCP_MEMORY_SQLITE_PATH` or `MCP_MEMORY_SQLITEVEC_PATH`: Path to `.db` file. Default `${BASE_DIR}/sqlite_vec.db`.
- `MCP_MEMORY_SQLITE_PRAGMAS`: CSV list of custom pragmas e.g. `busy_timeout=15000,cache_size=20000` (v8.9.0+ recommended values for concurrent access).
Chroma options:
- `MCP_MEMORY_CHROMADB_HOST`: Hostname for remote Chroma.
- `MCP_MEMORY_CHROMADB_PORT`: Port (default 8000).
- `MCP_MEMORY_CHROMADB_SSL`: `true|false` for HTTPS.
- `MCP_MEMORY_CHROMADB_API_KEY`: API key when remote.
- `MCP_MEMORY_COLLECTION_NAME`: Collection name (default `memory_collection`).
Cloudflare options (required unless otherwise noted):
- `CLOUDFLARE_API_TOKEN` (required)
- `CLOUDFLARE_ACCOUNT_ID` (required)
- `CLOUDFLARE_VECTORIZE_INDEX` (required)
- `CLOUDFLARE_D1_DATABASE_ID` (required)
- `CLOUDFLARE_R2_BUCKET` (optional, for large content)
- `CLOUDFLARE_EMBEDDING_MODEL` (default `@cf/baai/bge-base-en-v1.5`)
- `CLOUDFLARE_LARGE_CONTENT_THRESHOLD` (bytes; default 1,048,576)
- `CLOUDFLARE_MAX_RETRIES` (default 3)
- `CLOUDFLARE_BASE_DELAY` (seconds; default 1.0)
## Embedding Model
- `MCP_EMBEDDING_MODEL`: Model name (default `all-MiniLM-L6-v2`).
- `MCP_MEMORY_USE_ONNX`: `true|false` toggle for ONNX path.
## HTTP/HTTPS Interface
- `MCP_HTTP_ENABLED`: `true|false` to enable HTTP interface.
- `MCP_HTTP_HOST`: Bind address (default `0.0.0.0`).
- `MCP_HTTP_PORT`: Port (default `8000`).
- `MCP_CORS_ORIGINS`: Comma-separated origins (default `*`).
- `MCP_SSE_HEARTBEAT`: SSE heartbeat interval seconds (default 30).
- `MCP_API_KEY`: Optional API key for HTTP.
TLS:
- `MCP_HTTPS_ENABLED`: `true|false`.
- `MCP_SSL_CERT_FILE`, `MCP_SSL_KEY_FILE`: Certificate and key paths.
## mDNS Service Discovery
- `MCP_MDNS_ENABLED`: `true|false` (default `true`).
- `MCP_MDNS_SERVICE_NAME`: Service display name (default `MCP Memory Service`).
- `MCP_MDNS_SERVICE_TYPE`: Service type (default `_mcp-memory._tcp.local.`).
- `MCP_MDNS_DISCOVERY_TIMEOUT`: Seconds to wait for discovery (default 5).
## Consolidation (Optional)
- `MCP_CONSOLIDATION_ENABLED`: `true|false`.
- Archive location:
- `MCP_CONSOLIDATION_ARCHIVE_PATH` or `MCP_MEMORY_ARCHIVE_PATH` (default `${BASE_DIR}/consolidation_archive`).
- Config knobs:
- Decay: `MCP_DECAY_ENABLED`, retention by type: `MCP_RETENTION_CRITICAL`, `MCP_RETENTION_REFERENCE`, `MCP_RETENTION_STANDARD`, `MCP_RETENTION_TEMPORARY`.
- Associations: `MCP_ASSOCIATIONS_ENABLED`, `MCP_ASSOCIATION_MIN_SIMILARITY`, `MCP_ASSOCIATION_MAX_SIMILARITY`, `MCP_ASSOCIATION_MAX_PAIRS`.
- Clustering: `MCP_CLUSTERING_ENABLED`, `MCP_CLUSTERING_MIN_SIZE`, `MCP_CLUSTERING_ALGORITHM`.
- Compression: `MCP_COMPRESSION_ENABLED`, `MCP_COMPRESSION_MAX_LENGTH`, `MCP_COMPRESSION_PRESERVE_ORIGINALS`.
- Forgetting: `MCP_FORGETTING_ENABLED`, `MCP_FORGETTING_RELEVANCE_THRESHOLD`, `MCP_FORGETTING_ACCESS_THRESHOLD`.
- Scheduling (APScheduler-ready):
- `MCP_SCHEDULE_DAILY` (default `02:00`), `MCP_SCHEDULE_WEEKLY` (default `SUN 03:00`), `MCP_SCHEDULE_MONTHLY` (default `01 04:00`), `MCP_SCHEDULE_QUARTERLY` (default `disabled`), `MCP_SCHEDULE_YEARLY` (default `disabled`).
## Machine Identification
- `MCP_MEMORY_INCLUDE_HOSTNAME`: `true|false` to tag memories with `source:<hostname>` and include `hostname` metadata.
## Logging and Performance
- `LOG_LEVEL`: Root logging level (default `WARNING`).
- `DEBUG_MODE`: When unset, the service raises log levels for performance-critical libs (`chromadb`, `sentence_transformers`, `transformers`, `torch`, `numpy`).
## Recommended Defaults by Backend
- SQLite-vec:
- Defaults enable WAL, busy timeout, optimized cache; customize with `MCP_MEMORY_SQLITE_PRAGMAS`.
- For multi-client setups, the service auto-detects and may start/use an HTTP coordinator.
- ChromaDB:
- HNSW space/ef/M values tuned for balanced accuracy and speed; migration messaging warns of deprecation and recommends moving to SQLite-vec.
- Cloudflare:
- Ensure required variables are set or the process exits with a clear error and checklist.
```
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/performance_issue.yml:
--------------------------------------------------------------------------------
```yaml
name: Performance Issue
description: Report slow operations, memory leaks, or performance degradation
title: "[Performance]: "
labels: ["performance", "triage"]
body:
- type: markdown
attributes:
value: |
Thank you for reporting a performance issue! Detailed metrics help us diagnose and optimize the system.
- type: textarea
id: description
attributes:
label: Performance Issue Description
description: Describe the performance problem you're experiencing
placeholder: Memory search is taking >5 seconds when...
validations:
required: true
- type: dropdown
id: operation
attributes:
label: Affected Operation
description: Which operation is experiencing performance issues?
options:
- Memory Storage (store_memory)
- Memory Retrieval (recall_memory, retrieve)
- Search Operations (semantic/tag/time search)
- Document Ingestion (PDF/DOCX processing)
- Dashboard Loading (HTTP UI)
- Server Startup/Initialization
- Background Sync (hybrid backend)
- Database Operations (general)
- Other
validations:
required: true
- type: textarea
id: metrics
attributes:
label: Performance Metrics
description: Provide timing information or benchmarks
placeholder: |
Current performance: 10 seconds
Expected performance: <1 second
Tested with: 1000 memories, 5 concurrent requests
value: |
- Operation time:
- Memory count in database:
- Concurrent operations:
- CPU usage:
- Memory usage:
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: Steps to Reproduce
description: How can we reproduce this performance issue?
placeholder: |
1. Insert 10,000 memories using...
2. Run search query...
3. Observe >5 second response time
value: |
1.
2.
3.
validations:
required: true
- type: dropdown
id: storage-backend
attributes:
label: Storage Backend
description: Which backend shows the performance issue?
options:
- sqlite-vec (local)
- cloudflare (remote)
- hybrid (sqlite + cloudflare)
- All backends
- Unsure
validations:
required: true
- type: input
id: database-size
attributes:
label: Database Size
description: Number of memories and approximate disk size
placeholder: "5000 memories, 50MB database file"
validations:
required: true
- type: dropdown
id: trend
attributes:
label: Performance Trend
description: Is this a new issue or has it gotten worse over time?
options:
- Always been slow
- Recently degraded (specify version)
- Gets worse with more data
- Intermittent (sometimes fast, sometimes slow)
- After specific change/upgrade
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment Details
description: System specifications that might affect performance
placeholder: |
OS: macOS 14.1
CPU: M2 Max (12 cores)
RAM: 32GB
Disk: SSD (NVMe)
Python: 3.11.5
Version: v8.17.0
value: |
- OS:
- CPU:
- RAM:
- Disk Type:
- Python Version:
- MCP Version:
validations:
required: true
- type: textarea
id: profiling
attributes:
label: Profiling Data (Optional)
description: |
If you've profiled the operation, include results:
- Python cProfile output
- Database query EXPLAIN QUERY PLAN
- Network latency measurements (for remote backends)
placeholder: Paste profiling data here
render: shell
- type: textarea
id: logs
attributes:
label: Relevant Logs
description: Logs showing timing or performance warnings
placeholder: Paste logs with timestamps
render: shell
- type: textarea
id: workaround
attributes:
label: Workaround
description: Have you found any temporary workarounds?
placeholder: |
Reducing batch size helps...
Switching to different backend improves...
- type: checkboxes
id: checks
attributes:
label: Pre-submission Checklist
description: Please verify you've completed these steps
options:
- label: I've verified this is a performance regression (not expected behavior)
required: true
- label: I've included specific timing measurements (not just "it's slow")
required: true
- label: I've tested with latest version to confirm issue still exists
required: true
- label: I've described database size and environment specifications
required: true
```
--------------------------------------------------------------------------------
/claude-hooks/test-recency-scoring.js:
--------------------------------------------------------------------------------
```javascript
#!/usr/bin/env node
/**
* Test script to validate recency-focused scoring improvements
*/
const { scoreMemoryRelevance, calculateTimeDecay, calculateRecencyBonus } = require('./utilities/memory-scorer');
const config = require('./config.json');
// Test project context
const projectContext = {
name: 'mcp-memory-service',
language: 'Python',
frameworks: ['FastAPI'],
tools: ['pytest']
};
// Test memories with different ages
const testMemories = [
{
content: 'Fixed critical bug in HTTP protocol implementation for memory hooks',
tags: ['mcp-memory-service', 'bug-fix', 'http-protocol'],
memory_type: 'bug-fix',
created_at_iso: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString() // 3 days ago
},
{
content: 'Comprehensive README restructuring and organization completed successfully for MCP Memory Service project',
tags: ['mcp-memory-service', 'claude-code-reference', 'documentation'],
memory_type: 'reference',
created_at_iso: new Date(Date.now() - 60 * 24 * 60 * 60 * 1000).toISOString() // 60 days ago
},
{
content: 'Implemented dashboard dark mode with improved UX',
tags: ['mcp-memory-service', 'feature', 'dashboard'],
memory_type: 'feature',
created_at_iso: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString() // 5 days ago
},
{
content: 'CONTRIBUTING.md Structure - Created comprehensive contribution guidelines',
tags: ['mcp-memory-service', 'claude-code-reference', 'documentation'],
memory_type: 'reference',
created_at_iso: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString() // 30 days ago
},
{
content: 'Removed ChromaDB backend - major refactoring for v8.0',
tags: ['mcp-memory-service', 'refactor', 'architecture'],
memory_type: 'architecture',
created_at_iso: new Date(Date.now() - 4 * 24 * 60 * 60 * 1000).toISOString() // 4 days ago
}
];
// Calculate daysAgo once for each memory (DRY principle)
const memoriesWithAge = testMemories.map(mem => ({
...mem,
daysAgo: Math.floor((Date.now() - new Date(mem.created_at_iso)) / (1000 * 60 * 60 * 24))
}));
console.log('\n=== RECENCY SCORING TEST ===\n');
// Show decay and bonus calculations
console.log('📊 Time Decay and Recency Bonus Analysis:');
console.log('─'.repeat(80));
memoriesWithAge.forEach((mem, idx) => {
const decayScore = calculateTimeDecay(mem.created_at_iso, config.memoryScoring.timeDecayRate); // Using decay rate from config
const recencyBonus = calculateRecencyBonus(mem.created_at_iso);
console.log(`Memory ${idx + 1}: ${mem.daysAgo} days old`);
console.log(` Time Decay (${config.memoryScoring.timeDecayRate} rate): ${decayScore.toFixed(3)}`);
console.log(` Recency Bonus: ${recencyBonus > 0 ? '+' + recencyBonus.toFixed(3) : '0.000'}`);
console.log(` Content: ${mem.content.substring(0, 60)}...`);
console.log('');
});
// Score memories with new algorithm
console.log('\n📈 Final Scoring Results (New Algorithm):');
console.log('─'.repeat(80));
const scoredMemories = scoreMemoryRelevance(memoriesWithAge, projectContext, {
verbose: false,
weights: config.memoryScoring.weights,
timeDecayRate: config.memoryScoring.timeDecayRate
});
scoredMemories.forEach((memory, index) => {
console.log(`${index + 1}. Score: ${memory.relevanceScore.toFixed(3)} (${memory.daysAgo} days old)`);
console.log(` Content: ${memory.content.substring(0, 70)}...`);
console.log(` Breakdown:`);
console.log(` - Time Decay: ${memory.scoreBreakdown.timeDecay.toFixed(3)} (weight: ${config.memoryScoring.weights.timeDecay})`);
console.log(` - Tag Relevance: ${memory.scoreBreakdown.tagRelevance.toFixed(3)} (weight: ${config.memoryScoring.weights.tagRelevance})`);
console.log(` - Content Quality: ${memory.scoreBreakdown.contentQuality.toFixed(3)} (weight: ${config.memoryScoring.weights.contentQuality})`);
console.log(` - Recency Bonus: ${memory.scoreBreakdown.recencyBonus.toFixed(3)} (direct boost)`);
console.log('');
});
console.log('\n✅ Test Summary:');
console.log('─'.repeat(80));
console.log('Expected Behavior:');
console.log(' - Recent memories (3-5 days old) should rank higher');
console.log(' - Recency bonus (+0.15 for <7 days, +0.10 for <14 days, +0.05 for <30 days)');
console.log(' - Gentler time decay (0.05 rate vs old 0.1 rate)');
console.log(' - Higher time weight (0.40 vs old 0.25)');
console.log(' - Old memories with perfect tags should rank lower despite tag advantage\n');
// Check if recent memories are ranked higher
const top3 = scoredMemories.slice(0, 3);
const recentInTop3 = top3.filter(m => m.daysAgo <= 7).length;
if (recentInTop3 >= 2) {
console.log('✅ SUCCESS: At least 2 of top 3 memories are from the last week');
} else {
console.log('❌ ISSUE: Recent memories are not prioritized as expected');
}
console.log('');
```
--------------------------------------------------------------------------------
/tests/integration/test_http_server_startup.py:
--------------------------------------------------------------------------------
```python
"""
Integration tests for HTTP server startup.
These tests verify that the HTTP server can actually start and respond,
catching issues like import-time errors, syntax errors, and module loading problems.
Added to prevent production bugs like those in v8.12.0 where 3 critical bugs
made it past 55 unit tests because we had zero HTTP server integration tests.
"""
import pytest
from fastapi.testclient import TestClient
def test_http_server_starts():
"""Test that server imports and starts without errors.
This test catches:
- Import-time evaluation errors (like get_storage() called at import)
- Syntax errors in route handlers
- Module loading failures
"""
from mcp_memory_service.web.app import app
client = TestClient(app)
response = client.get("/api/health")
assert response.status_code == 200
data = response.json()
assert data["status"] == "healthy"
assert "version" in data
def test_server_modules_importable():
"""Test that all server modules can be imported without errors.
This catches syntax errors and import-time failures in module code.
"""
# Core dependencies module
import mcp_memory_service.web.dependencies
assert hasattr(mcp_memory_service.web.dependencies, 'get_storage')
assert hasattr(mcp_memory_service.web.dependencies, 'get_memory_service')
# API endpoint modules
import mcp_memory_service.web.api.memories
import mcp_memory_service.web.api.search
import mcp_memory_service.web.api.health
import mcp_memory_service.web.api.manage
# Main app module
import mcp_memory_service.web.app
assert hasattr(mcp_memory_service.web.app, 'app')
def test_all_api_routes_registered():
"""Test that all expected API routes are registered.
This ensures route registration didn't fail silently.
"""
from mcp_memory_service.web.app import app
# Get all registered routes
routes = [route.path for route in app.routes]
# Essential routes that should always be present
essential_routes = [
"/api/health",
"/api/memories",
"/api/search",
"/api/tags",
]
for route in essential_routes:
assert any(r.startswith(route) for r in routes), f"Route {route} not registered"
def test_health_endpoint_responds():
"""Test that health endpoint returns valid response structure.
This is our canary - if this fails, server is broken.
"""
from mcp_memory_service.web.app import app
client = TestClient(app)
response = client.get("/api/health")
assert response.status_code == 200
data = response.json()
# Verify response structure
assert isinstance(data, dict)
assert "status" in data
assert "version" in data
assert "timestamp" in data
# Verify values are sensible
assert data["status"] in ["healthy", "degraded"]
assert isinstance(data["version"], str)
assert len(data["version"]) > 0
def test_cors_middleware_configured():
"""Test that CORS middleware is properly configured.
This prevents issues with web dashboard access.
"""
from mcp_memory_service.web.app import app
client = TestClient(app)
# Test CORS with actual GET request (OPTIONS may not be supported on all endpoints)
response = client.get(
"/api/health",
headers={"Origin": "http://localhost:3000"}
)
# Should have CORS headers (FastAPI's CORSMiddleware adds these)
assert response.status_code == 200
# Check for CORS headers in response
assert "access-control-allow-origin" in response.headers or response.status_code == 200
def test_static_files_mounted():
"""Test that static files (dashboard) are properly mounted."""
from mcp_memory_service.web.app import app
client = TestClient(app)
# Try to access root (should serve index.html)
response = client.get("/")
# Should return HTML content (status 200) or redirect
assert response.status_code in [200, 307, 308]
if response.status_code == 200:
assert "text/html" in response.headers.get("content-type", "")
def test_server_handles_404():
"""Test that server returns proper 404 for non-existent routes."""
from mcp_memory_service.web.app import app
client = TestClient(app)
response = client.get("/api/nonexistent-route-that-should-not-exist")
assert response.status_code == 404
def test_server_handles_invalid_json():
"""Test that server handles malformed JSON requests gracefully."""
from mcp_memory_service.web.app import app
client = TestClient(app)
# Send malformed JSON
response = client.post(
"/api/memories",
data="{'this': 'is not valid json}", # Missing quote on 'json'
headers={"Content-Type": "application/json"}
)
# Should return 400 or 422, not 500
assert response.status_code in [400, 422]
if __name__ == "__main__":
# Allow running tests directly for quick verification
pytest.main([__file__, "-v"])
```
--------------------------------------------------------------------------------
/docs/ide-compatability.md:
--------------------------------------------------------------------------------
```markdown
## IDE Compatibility
[](https://claude.ai)
[](https://cursor.sh)
[](https://codeium.com/windsurf)
[](https://github.com/saoudrizwan/claude-dev)
[](https://roo.ai)
As of June 2025, MCP (Model Context Protocol) has become the standard for AI-IDE integrations. The MCP Memory Service is **fully compatible** with all major AI-powered development environments:
### Supported IDEs
| IDE | MCP Support | Configuration Location | Notes |
|-----|------------|----------------------|--------|
| **Claude Desktop** | ✅ Full | `claude_desktop_config.json` | Official MCP support |
| **Claude Code** | ✅ Full | CLI configuration | Official MCP support |
| **Cursor** | ✅ Full | `.cursor/mcp.json` or global config | Supports stdio, SSE, HTTP |
| **WindSurf** | ✅ Full | MCP config file | Built-in server management |
| **Cline** | ✅ Full | VS Code MCP config | Can create/share MCP servers |
| **RooCode** | ✅ Full | IDE config | Full MCP client implementation |
| **VS Code** | ✅ Full | `.vscode/mcp.json` | Via MCP extension |
| **Zed** | ✅ Full | Built-in config | Native MCP support |
### Quick Setup for Popular IDEs
#### Cursor
Add to `.cursor/mcp.json` in your project or global Cursor config:
```json
{
"mcpServers": {
"memory": {
"command": "uv",
"args": [
"--directory",
"/path/to/mcp-memory-service",
"run",
"memory"
],
"env": {
"MCP_MEMORY_CHROMA_PATH": "/path/to/chroma_db",
"MCP_MEMORY_BACKUPS_PATH": "/path/to/backups"
}
}
}
}
```
#### WindSurf
WindSurf offers the easiest setup with built-in server management. Add to your WindSurf MCP configuration:
```json
{
"mcpServers": {
"memory": {
"command": "uv",
"args": ["--directory", "/path/to/mcp-memory-service", "run", "memory"],
"env": {
"MCP_MEMORY_CHROMA_PATH": "/path/to/chroma_db",
"MCP_MEMORY_BACKUPS_PATH": "/path/to/backups"
}
}
}
}
```
#### Cline (VS Code)
1. Open the Cline extension in VS Code
2. Click the MCP Servers icon
3. Click "Configure MCP Servers"
4. Add the memory service configuration (same format as above)
#### RooCode
RooCode uses a similar configuration format. Refer to RooCode's MCP documentation for the exact config file location.
### Working with Multiple MCP Servers
MCP servers are designed to be composable. You can use the Memory Service alongside other popular MCP servers:
```json
{
"mcpServers": {
"memory": {
"command": "uv",
"args": ["--directory", "/path/to/mcp-memory-service", "run", "memory"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "your-github-token"
}
},
"task-master": {
"command": "npx",
"args": ["-y", "task-master-mcp"]
}
}
}
```
### Alternative Installation Methods
#### Using NPX (if published to npm)
```json
{
"mcpServers": {
"memory": {
"command": "npx",
"args": ["-y", "@doobidoo/mcp-memory-service"]
}
}
}
```
#### Using Python directly
```json
{
"mcpServers": {
"memory": {
"command": "python",
"args": ["/path/to/mcp-memory-service/memory_wrapper.py"]
}
}
}
```
### Why Choose MCP Memory Service?
Unlike IDE-specific memory solutions, MCP Memory Service offers:
- **Cross-IDE Compatibility**: Your memories work across ALL supported IDEs
- **Persistent Storage**: Memories survive IDE restarts and updates
- **Semantic Search**: Find memories by meaning, not just keywords
- **Natural Language Time Queries**: "What did I work on last week?"
- **Tag-based Organization**: Organize memories with flexible tagging
- **Cross-Platform**: Works on macOS, Windows, and Linux
### Troubleshooting IDE Connections
If the memory service isn't connecting in your IDE:
1. **Verify Installation**: Run `python scripts/test_installation.py`
2. **Check Logs**: Most IDEs show MCP server logs in their output panels
3. **Test Standalone**: Try running the server directly: `uv run memory`
4. **Path Issues**: Use absolute paths in your configuration
5. **Python Environment**: Ensure the IDE can access your Python environment
### IDE-Specific Tips
**Cursor**: If using multiple MCP servers, be aware of Cursor's server limit. Prioritize based on your needs.
**WindSurf**: Take advantage of WindSurf's built-in server management UI for easier configuration.
**Cline**: Cline can display MCP server status - check for green indicators after configuration.
**VS Code with MCP Extension**: Install the official MCP extension from the marketplace for better integration.
```
--------------------------------------------------------------------------------
/archive/docs-removed-2025-08-23/master-guide.md:
--------------------------------------------------------------------------------
```markdown
# MCP Memory Service - Installation Guide
**Version**: 0.2.2+
**Last Updated**: 2025-07-26
**Supports**: ChromaDB + SQLite-vec backends, HTTP/SSE API
## Prerequisites
- Python 3.10 or newer
- pip (latest version recommended)
- A virtual environment (venv or conda)
- Git (to clone the repository)
## Quick Installation by Hardware Type
### 🖥️ Legacy Hardware (2013-2017 Intel Macs)
**Best for**: 2015 MacBook Pro, older Intel Macs without GPU
```bash
git clone https://github.com/doobidoo/mcp-memory-service.git
cd mcp-memory-service
python install.py --legacy-hardware
```
**What this does:**
- ✅ Detects older Intel Mac hardware
- ✅ Recommends SQLite-vec backend (lightweight)
- ✅ Uses Homebrew PyTorch if available (better compatibility)
- ✅ Configures ONNX runtime for CPU-only inference
- ✅ Optimizes for limited memory systems
### 🚀 Modern Hardware (2018+ Macs, GPU-enabled systems)
**Best for**: M1/M2/M3 Macs, modern Intel systems, Windows with GPU
```bash
git clone https://github.com/doobidoo/mcp-memory-service.git
cd mcp-memory-service
python install.py
```
**What this does:**
- ✅ Auto-detects available hardware acceleration
- ✅ Recommends ChromaDB for full features
- ✅ Configures GPU acceleration (CUDA/MPS/DirectML)
- ✅ Installs latest PyTorch and sentence-transformers
### 🌐 Server/Headless Installation
**Best for**: Linux servers, Docker deployments, CI/CD
```bash
git clone https://github.com/doobidoo/mcp-memory-service.git
cd mcp-memory-service
python install.py --server-mode --storage-backend sqlite_vec
```
## Standard Installation Steps
### 1. Clone and Setup Environment
```bash
git clone https://github.com/doobidoo/mcp-memory-service.git
cd mcp-memory-service
# Create and activate virtual environment
python -m venv venv
# On Windows
venv\Scripts\activate
# On macOS/Linux
source venv/bin/activate
```
### 2. Run Installation
```bash
python install.py
```
🌟 **Multi-Client Setup**: The installer will automatically detect MCP applications (Claude Desktop, VS Code, Continue, etc.) and offer to configure shared memory access. Choose 'y' for universal multi-client setup.
### 3. Verify Installation
```bash
python scripts/verify_environment.py
```
## Docker Installation
For cross-platform deployment:
```bash
# Clone repository
git clone https://github.com/doobidoo/mcp-memory-service.git
cd mcp-memory-service
# Build and run with Docker Compose
docker-compose up -d
```
## Command Line Options
### Basic Usage
```bash
python install.py [OPTIONS]
```
### Core Options
| Option | Description | Example |
|--------|-------------|---------|
| `--dev` | Install in development mode | `python install.py --dev` |
| `--chroma-path PATH` | Custom ChromaDB storage path | `python install.py --chroma-path /custom/path` |
| `--backups-path PATH` | Custom backup storage path | `python install.py --backups-path /custom/backups` |
### Storage Backend Options
| Backend | Description | Best For |
|---------|-------------|----------|
| `chromadb` | Full-featured vector database | High-memory systems, full features |
| `sqlite_vec` | Lightweight alternative | Resource-constrained systems |
| `auto_detect` | Auto-selection with fallback | Uncertain hardware capabilities |
```bash
# Force SQLite-vec backend
python install.py --storage-backend sqlite_vec
# Force ChromaDB backend
python install.py --storage-backend chromadb
# Auto-detection with fallback
python install.py --storage-backend auto_detect
```
### Hardware-Specific Options
| Option | Description | Use Case |
|--------|-------------|----------|
| `--legacy-hardware` | Optimize for older systems | 2013-2017 Intel Macs |
| `--server-mode` | Headless server installation | Linux servers, Docker |
| `--force-cpu` | Disable GPU acceleration | Troubleshooting GPU issues |
### Multi-Client Options
| Option | Description | Example |
|--------|-------------|---------|
| `--multi-client` | Enable shared memory access | `python install.py --multi-client` |
| `--claude-only` | Configure for Claude Desktop only | `python install.py --claude-only` |
### Claude Code Integration (v2.2.0)
| Option | Description | Example |
|--------|-------------|---------|
| `--install-claude-commands` | Install conversational memory commands | `python install.py --install-claude-commands` |
| `--skip-claude-commands-prompt` | Skip interactive commands installation prompt | `python install.py --skip-claude-commands-prompt` |
## Platform-Specific Installation
- **Windows**: See [windows-setup.md](../platforms/windows.md)
- **Ubuntu/Linux**: See [ubuntu-setup.md](../platforms/ubuntu.md)
- **macOS Intel (Legacy)**: See [macos-intel.md](../platforms/macos-intel.md)
## Troubleshooting
Common installation issues and solutions can be found in [troubleshooting.md](../troubleshooting/general.md).
## Next Steps
After installation:
1. Configure your MCP client (Claude Desktop, VS Code, etc.)
2. Test the connection with `python scripts/test-connection.py`
3. Read the [User Guide](../guides/claude_integration.md) for usage instructions
```
--------------------------------------------------------------------------------
/scripts/development/remote_ingest.sh:
--------------------------------------------------------------------------------
```bash
#!/bin/bash
# Remote Document Ingestion Script for MCP Memory Service
# This script uploads and ingests documents on the remote server
set -e
# Configuration
REMOTE_HOST="${REMOTE_HOST:-10.0.1.30}"
REMOTE_USER="${REMOTE_USER:-hkr}"
# Auto-detect the mcp-memory-service repository location
REMOTE_PATH=$(ssh ${REMOTE_USER}@${REMOTE_HOST} "find /home/${REMOTE_USER} -iname 'mcp-memory-service' -type d -exec test -f {}/pyproject.toml \; -print 2>/dev/null | head -n1")
REMOTE_PATH="${REMOTE_PATH:-/home/${REMOTE_USER}/repositories/mcp-memory-service}"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_info() {
echo -e "${BLUE}ℹ️ $1${NC}"
}
print_success() {
echo -e "${GREEN}✅ $1${NC}"
}
print_error() {
echo -e "${RED}❌ $1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
# Function to show usage
usage() {
cat << EOF
Usage: $0 [OPTIONS] <file_or_directory>
Remote document ingestion for MCP Memory Service
Options:
-t, --tags TAGS Comma-separated tags to apply (default: "documentation")
-c, --chunk-size SIZE Chunk size in characters (default: 800)
-r, --recursive Process directories recursively
-e, --extensions EXTS File extensions to process (default: all supported)
-h, --host HOST Remote host (default: 10.0.1.30)
-u, --user USER Remote user (default: hkr)
--help Show this help message
Examples:
# Ingest a single file
$0 README.md
# Ingest with custom tags
$0 -t "documentation,important" CLAUDE.md
# Ingest entire directory
$0 -r docs/
# Ingest specific file types
$0 -r -e "md,txt" docs/
EOF
exit 0
}
# Parse command line arguments
TAGS="documentation"
CHUNK_SIZE="800"
RECURSIVE=""
EXTENSIONS=""
FILES=()
while [[ $# -gt 0 ]]; do
case $1 in
-t|--tags)
TAGS="$2"
shift 2
;;
-c|--chunk-size)
CHUNK_SIZE="$2"
shift 2
;;
-r|--recursive)
RECURSIVE="--recursive"
shift
;;
-e|--extensions)
EXTENSIONS="--extensions $2"
shift 2
;;
-h|--host)
REMOTE_HOST="$2"
shift 2
;;
-u|--user)
REMOTE_USER="$2"
shift 2
;;
--help)
usage
;;
*)
FILES+=("$1")
shift
;;
esac
done
# Check if files were provided
if [ ${#FILES[@]} -eq 0 ]; then
print_error "No files or directories specified"
usage
fi
# Process each file/directory
for item in "${FILES[@]}"; do
if [ ! -e "$item" ]; then
print_error "File or directory not found: $item"
continue
fi
# Get absolute path
ITEM_PATH=$(realpath "$item")
ITEM_NAME=$(basename "$item")
print_info "Processing: $ITEM_NAME"
if [ -f "$item" ]; then
# Single file ingestion
print_info "Uploading file to remote server..."
# Create temp directory on remote
REMOTE_TEMP=$(ssh ${REMOTE_USER}@${REMOTE_HOST} "mktemp -d")
# Upload file
scp -q "$ITEM_PATH" ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_TEMP}/
# Run ingestion on remote
print_info "Running remote ingestion..."
ssh ${REMOTE_USER}@${REMOTE_HOST} "cd \"${REMOTE_PATH}\" && \
.venv/bin/python -m mcp_memory_service.cli.main ingest-document \
${REMOTE_TEMP}/${ITEM_NAME} \
--tags '${TAGS}' \
--chunk-size ${CHUNK_SIZE} \
--verbose 2>&1 | grep -E '✅|📄|💾|⚡|⏱️'"
# Cleanup
ssh ${REMOTE_USER}@${REMOTE_HOST} "rm -rf ${REMOTE_TEMP}"
print_success "Completed ingestion of $ITEM_NAME"
elif [ -d "$item" ]; then
# Directory ingestion
print_info "Uploading directory to remote server..."
# Create temp directory on remote
REMOTE_TEMP=$(ssh ${REMOTE_USER}@${REMOTE_HOST} "mktemp -d")
# Upload directory
scp -rq "$ITEM_PATH" ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_TEMP}/
# Run ingestion on remote
print_info "Running remote directory ingestion..."
ssh ${REMOTE_USER}@${REMOTE_HOST} "cd \"${REMOTE_PATH}\" && \
.venv/bin/python -m mcp_memory_service.cli.main ingest-directory \
${REMOTE_TEMP}/${ITEM_NAME} \
--tags '${TAGS}' \
--chunk-size ${CHUNK_SIZE} \
${RECURSIVE} \
${EXTENSIONS} \
--verbose 2>&1 | grep -E '✅|📁|📄|💾|⚡|⏱️|❌'"
# Cleanup
ssh ${REMOTE_USER}@${REMOTE_HOST} "rm -rf ${REMOTE_TEMP}"
print_success "Completed ingestion of directory $ITEM_NAME"
fi
done
print_success "Remote ingestion complete!"
print_info "View memories at: https://${REMOTE_HOST}:8443/"
```
--------------------------------------------------------------------------------
/scripts/sync/sync_now.py:
--------------------------------------------------------------------------------
```python
#!/usr/bin/env python3
"""
Manual sync utility for Hybrid Storage Backend.
Triggers an immediate sync from SQLite-vec to Cloudflare.
Usage:
python sync_now.py [--db-path PATH] [--verbose]
Environment Variables:
MCP_MEMORY_SQLITE_PATH: Override default database path
"""
import asyncio
import argparse
import logging
import os
import sys
from pathlib import Path
from typing import TypedDict
try:
from dotenv import load_dotenv
from mcp_memory_service.storage.factory import create_storage_instance
from mcp_memory_service.storage.hybrid import HybridMemoryStorage
from mcp_memory_service.config import SQLITE_VEC_PATH
except ImportError as e:
print(f"❌ Import error: {e}", file=sys.stderr)
print("Make sure the package is installed: pip install -e .", file=sys.stderr)
sys.exit(1)
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(message)s'
)
logger = logging.getLogger(__name__)
class SyncResult(TypedDict, total=False):
"""Type-safe structure for sync operation results."""
status: str
synced_to_secondary: int
duration: float
failed: int
error: str
class SyncStatus(TypedDict, total=False):
"""Type-safe structure for sync status information."""
queue_size: int
cloudflare_available: bool
failed_operations: int
async def main(db_path: str | None = None, verbose: bool = False) -> int:
"""
Run immediate sync.
Args:
db_path: Optional path to SQLite database. If not provided,
uses MCP_MEMORY_SQLITE_PATH env var or default config.
verbose: Enable verbose error reporting with full tracebacks.
Returns:
0 on success, 1 on failure
"""
# Load environment variables
load_dotenv()
logger.info("🔄 Starting manual sync...")
# Determine database path
sqlite_path = Path(db_path or os.getenv('MCP_MEMORY_SQLITE_PATH') or SQLITE_VEC_PATH)
if not sqlite_path.exists():
logger.error(f"❌ Database not found: {sqlite_path}")
return 1
logger.info(f"📁 Using database: {sqlite_path}")
# Create storage instance
try:
storage = await create_storage_instance(str(sqlite_path))
except (ValueError, RuntimeError, FileNotFoundError, OSError) as e:
logger.error(f"❌ Failed to create storage instance: {e}")
if verbose:
logger.exception("Full traceback for failed storage instance creation:")
return 1
# Type-safe check for hybrid storage
if not isinstance(storage, HybridMemoryStorage):
logger.error("❌ Not a hybrid backend - sync not available")
logger.error(f" Found: {storage.__class__.__name__}")
return 1
# Get sync status before
try:
status_before = await storage.get_sync_status()
logger.info(f"📊 Before sync:")
logger.info(f" Queue size: {status_before['queue_size']}")
logger.info(f" Cloudflare available: {status_before['cloudflare_available']}")
except Exception as e:
logger.warning(f"⚠️ Could not get sync status: {e}")
# Trigger immediate sync
logger.info("\n⏳ Triggering sync...")
try:
result = await storage.force_sync()
# Check sync status
if result.get('status') != 'completed':
logger.error(f"❌ Sync failed with status: {result.get('status')}")
if result.get('error'):
logger.error(f" Error: {result.get('error')}")
return 1
logger.info("✅ Sync completed successfully!")
logger.info(f" Synced: {result.get('synced_to_secondary', 0)} operations")
logger.info(f" Duration: {result.get('duration', 0):.2f}s")
# Report any failed operations
if result.get('failed', 0) > 0:
logger.warning(f" ⚠️ Failed operations: {result.get('failed', 0)}")
except Exception as e:
logger.error(f"❌ Sync failed: {e}")
if verbose:
logger.exception("Full traceback for sync failure:")
return 1
# Get sync status after
try:
status_after = await storage.get_sync_status()
logger.info(f"\n📊 After sync:")
logger.info(f" Queue size: {status_after['queue_size']}")
logger.info(f" Failed operations: {status_after['failed_operations']}")
except Exception as e:
logger.warning(f"⚠️ Could not get final sync status: {e}")
return 0
def parse_args():
"""Parse command line arguments."""
parser = argparse.ArgumentParser(
description="Manual sync utility for Hybrid Storage Backend"
)
parser.add_argument(
'--db-path',
type=str,
help='Path to SQLite database (default: from config or env)'
)
parser.add_argument(
'--verbose', '-v',
action='store_true',
help='Enable verbose error reporting with full tracebacks'
)
return parser.parse_args()
if __name__ == "__main__":
args = parse_args()
exit_code = asyncio.run(main(db_path=args.db_path, verbose=args.verbose))
sys.exit(exit_code)
```
--------------------------------------------------------------------------------
/scripts/sync/litestream/memory_sync.sh:
--------------------------------------------------------------------------------
```bash
#!/bin/bash
# Main memory synchronization orchestrator
# Implements Git-like workflow: stash → pull → apply → push
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
STAGING_DB="/Users/hkr/Library/Application Support/mcp-memory/sqlite_vec_staging.db"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
print_header() {
echo -e "${BLUE}=== $1 ===${NC}"
}
print_success() {
echo -e "${GREEN}✓ $1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠ $1${NC}"
}
print_error() {
echo -e "${RED}✗ $1${NC}"
}
show_status() {
print_header "Memory Sync Status"
if [ ! -f "$STAGING_DB" ]; then
echo "Staging database not initialized"
return
fi
STAGED_COUNT=$(sqlite3 "$STAGING_DB" "SELECT COUNT(*) FROM staged_memories WHERE conflict_status = 'none';" 2>/dev/null || echo "0")
CONFLICT_COUNT=$(sqlite3 "$STAGING_DB" "SELECT COUNT(*) FROM staged_memories WHERE conflict_status = 'detected';" 2>/dev/null || echo "0")
FAILED_COUNT=$(sqlite3 "$STAGING_DB" "SELECT COUNT(*) FROM staged_memories WHERE conflict_status = 'push_failed';" 2>/dev/null || echo "0")
LAST_REMOTE_SYNC=$(sqlite3 "$STAGING_DB" "SELECT value FROM sync_status WHERE key = 'last_remote_sync';" 2>/dev/null || echo "Never")
LAST_PUSH=$(sqlite3 "$STAGING_DB" "SELECT value FROM sync_status WHERE key = 'last_push_attempt';" 2>/dev/null || echo "Never")
echo "Staged changes ready: $STAGED_COUNT"
echo "Conflicts detected: $CONFLICT_COUNT"
echo "Failed pushes: $FAILED_COUNT"
echo "Last remote sync: $LAST_REMOTE_SYNC"
echo "Last push attempt: $LAST_PUSH"
}
full_sync() {
print_header "Starting Full Memory Sync"
# Step 1: Stash local changes
print_header "Step 1: Stashing Local Changes"
if ! "$SCRIPT_DIR/stash_local_changes.sh"; then
print_error "Failed to stash local changes"
return 1
fi
print_success "Local changes stashed"
# Step 2: Pull remote changes
print_header "Step 2: Pulling Remote Changes"
if ! "$SCRIPT_DIR/pull_remote_changes.sh"; then
print_error "Failed to pull remote changes"
return 1
fi
print_success "Remote changes pulled"
# Step 3: Apply staged changes
print_header "Step 3: Applying Staged Changes"
if ! "$SCRIPT_DIR/apply_local_changes.sh"; then
print_warning "Some issues applying staged changes (check output above)"
else
print_success "Staged changes applied"
fi
# Step 4: Push remaining changes to remote
print_header "Step 4: Pushing to Remote API"
if ! "$SCRIPT_DIR/push_to_remote.sh"; then
print_warning "Some issues pushing to remote (check output above)"
else
print_success "Changes pushed to remote"
fi
print_header "Full Sync Completed"
show_status
}
quick_push() {
print_header "Quick Push to Remote"
if ! "$SCRIPT_DIR/push_to_remote.sh"; then
print_error "Push failed"
return 1
fi
print_success "Quick push completed"
show_status
}
quick_pull() {
print_header "Quick Pull from Remote"
if ! "$SCRIPT_DIR/pull_remote_changes.sh"; then
print_error "Pull failed"
return 1
fi
print_success "Quick pull completed"
show_status
}
show_help() {
echo "Memory Sync Tool - Git-like workflow for MCP Memory Service"
echo ""
echo "Usage: $0 [command]"
echo ""
echo "Commands:"
echo " sync, full - Full synchronization (stash → pull → apply → push)"
echo " status, st - Show current sync status"
echo " push - Push staged changes to remote API"
echo " pull - Pull latest changes from remote"
echo " stash - Stash local changes to staging area"
echo " apply - Apply staged changes to local database"
echo " init - Initialize staging database"
echo " help, -h - Show this help message"
echo ""
echo "Examples:"
echo " $0 sync # Full synchronization workflow"
echo " $0 status # Check sync status"
echo " $0 push # Push staged changes only"
echo ""
echo "Environment Variables:"
echo " MCP_API_KEY - API key for remote server authentication"
}
# Main command handling
case "${1:-status}" in
"sync"|"full")
full_sync
;;
"status"|"st")
show_status
;;
"push")
quick_push
;;
"pull")
quick_pull
;;
"stash")
print_header "Stashing Changes"
"$SCRIPT_DIR/stash_local_changes.sh"
;;
"apply")
print_header "Applying Changes"
"$SCRIPT_DIR/apply_local_changes.sh"
;;
"init")
print_header "Initializing Staging Database"
"$SCRIPT_DIR/init_staging_db.sh"
;;
"help"|"-h"|"--help")
show_help
;;
*)
echo "Unknown command: $1"
echo "Use '$0 help' for usage information"
exit 1
;;
esac
```
--------------------------------------------------------------------------------
/scripts/database/check_sqlite_vec_status.py:
--------------------------------------------------------------------------------
```python
#!/usr/bin/env python3
"""
Check the status of SQLite-vec database and identify issues.
"""
import sqlite3
import sys
import os
def check_sqlite_vec_status(db_path):
"""Check the status of the SQLite-vec database."""
print(f"Checking SQLite-vec database: {db_path}")
print("="*60)
if not os.path.exists(db_path):
print(f"❌ Database not found: {db_path}")
return False
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
issues_found = []
try:
# Check basic tables
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
tables = [row[0] for row in cursor.fetchall()]
print(f"Tables: {', '.join(tables)}")
if 'memories' not in tables:
issues_found.append("Missing 'memories' table")
else:
cursor.execute("SELECT COUNT(*) FROM memories")
memory_count = cursor.fetchone()[0]
print(f"📝 Memories: {memory_count}")
if 'memory_embeddings' not in tables:
issues_found.append("Missing 'memory_embeddings' table")
else:
# Try to access the embeddings table
try:
cursor.execute("SELECT COUNT(*) FROM memory_embeddings")
embedding_count = cursor.fetchone()[0]
print(f"🧠 Embeddings: {embedding_count}")
# Check if counts match
if 'memories' in tables:
if memory_count != embedding_count:
issues_found.append(f"Count mismatch: {memory_count} memories vs {embedding_count} embeddings")
except Exception as e:
if "no such module: vec0" in str(e):
issues_found.append("sqlite-vec extension not loaded - cannot access embeddings")
else:
issues_found.append(f"Cannot access embeddings table: {e}")
# Check if extension loading is possible
try:
conn.enable_load_extension(True)
extension_support = True
except:
extension_support = False
issues_found.append("Extension loading not supported")
print(f"Extension loading: {'✅ Supported' if extension_support else '❌ Not supported'}")
# Try to load sqlite-vec
if extension_support:
try:
# This will fail if sqlite-vec is not installed
import sqlite_vec
sqlite_vec.load(conn)
print("✅ sqlite-vec extension loaded successfully")
# Now try to access embeddings
try:
cursor.execute("SELECT COUNT(*) FROM memory_embeddings")
embedding_count = cursor.fetchone()[0]
print(f"✅ Can now access embeddings: {embedding_count}")
# Test a simple search
if embedding_count > 0:
cursor.execute("SELECT * FROM memory_embeddings LIMIT 1")
row = cursor.fetchone()
print("✅ Embedding data accessible")
except Exception as e:
issues_found.append(f"Still cannot access embeddings after loading extension: {e}")
except ImportError:
issues_found.append("sqlite-vec Python module not installed")
except Exception as e:
issues_found.append(f"Failed to load sqlite-vec extension: {e}")
except Exception as e:
issues_found.append(f"Database error: {e}")
finally:
conn.close()
print("\n" + "="*60)
if issues_found:
print("⚠️ Issues Found:")
for i, issue in enumerate(issues_found, 1):
print(f" {i}. {issue}")
print("\nRecommendations:")
if "sqlite-vec Python module not installed" in str(issues_found):
print(" • Install sqlite-vec: uv pip install sqlite-vec")
if "sentence-transformers" in str(issues_found) or "embedding" in str(issues_found).lower():
print(" • Install sentence-transformers: uv pip install sentence-transformers torch")
if "Count mismatch" in str(issues_found):
print(" • Run repair script to regenerate missing embeddings")
if "cannot access embeddings" in str(issues_found).lower():
print(" • Database may need migration to fix schema issues")
else:
print("✅ No issues found - database appears healthy!")
return len(issues_found) == 0
def main():
if len(sys.argv) != 2:
print("Usage: python check_sqlite_vec_status.py <database_path>")
sys.exit(1)
db_path = sys.argv[1]
healthy = check_sqlite_vec_status(db_path)
sys.exit(0 if healthy else 1)
if __name__ == "__main__":
main()
```
--------------------------------------------------------------------------------
/docs/IMPLEMENTATION_PLAN_HTTP_SSE.md:
--------------------------------------------------------------------------------
```markdown
# HTTP/SSE + SQLite-vec Implementation Plan
**Date**: 2025-07-25
**Status**: Extracted from previous planning session
**Context**: Issue #57 - Add HTTP/SSE interface to MCP Memory Service
## Executive Summary
Implement HTTP REST API and Server-Sent Events (SSE) interface for the MCP Memory Service using the existing sqlite-vec backend instead of ChromaDB. This creates a lightweight, edge-ready solution that maintains all existing MCP functionality while adding modern web capabilities.
## Key Architectural Decision
**Combine HTTP/SSE with sqlite-vec backend** instead of ChromaDB for:
- Simplified deployment (single file database)
- Better performance (10x faster operations)
- Edge-ready deployment (Cloudflare Workers, Vercel)
- No external dependencies
- Instant SSE updates via SQLite triggers
## Implementation Phases
### Phase 1: Foundation (Week 1)
- ✅ Create feature branch from sqlite-vec-backend
- ✅ Create PROJECT_STATUS.md tracking file
- [ ] Validate sqlite-vec functionality
- [ ] Add FastAPI dependencies
- [ ] Set up development environment
### Phase 2: HTTP Implementation (Week 2)
- [ ] Create web server structure
- [ ] Implement health check endpoint
- [ ] Add memory CRUD endpoints
- [ ] Add search endpoints
- [ ] OpenAPI documentation
### Phase 3: SSE Implementation (Week 3)
- [ ] Design SSE event architecture
- [ ] Implement SQLite triggers
- [ ] Create /events endpoint
- [ ] Connection management
- [ ] Real-time update testing
### Phase 4: Dashboard (Week 4)
- [ ] Minimal UI design (vanilla JS)
- [ ] Memory visualization
- [ ] SSE connection handling
- [ ] Search interface
- [ ] Responsive design
## Technical Architecture
### Directory Structure
```
src/mcp_memory_service/
├── web/
│ ├── __init__.py
│ ├── app.py # FastAPI application
│ ├── sse.py # SSE event handling
│ ├── api/
│ │ ├── __init__.py
│ │ ├── memories.py # Memory CRUD
│ │ ├── search.py # Search operations
│ │ └── health.py # Health monitoring
│ └── static/
│ ├── index.html # Dashboard
│ ├── app.js # Frontend JS
│ └── style.css # Styling
├── storage/
│ ├── sqlite_vec.py # Existing
│ └── sqlite_sse.py # New: SSE triggers
```
### Server Modes
1. **MCP Mode**: Original stdio protocol (unchanged)
2. **HTTP Mode**: FastAPI server with SSE
3. **Hybrid Mode**: Both protocols simultaneously
### SSE Events
- `memory_stored`: New memory added
- `memory_deleted`: Memory removed
- `search_completed`: Search results ready
- `backup_status`: Backup progress
- `health_update`: System status changes
### API Endpoints
- `GET /api/health` - Health check
- `GET /api/memories` - List memories (paginated)
- `POST /api/memories` - Store new memory
- `GET /api/memories/{id}` - Get specific memory
- `DELETE /api/memories/{id}` - Delete memory
- `POST /api/search` - Semantic search
- `POST /api/search/by-tag` - Tag search
- `POST /api/search/by-time` - Time-based recall
- `GET /events` - SSE endpoint
## Dependencies to Add
```
fastapi>=0.115.0
uvicorn>=0.30.0
python-multipart>=0.0.9
sse-starlette>=2.1.0
aiofiles>=23.2.1
```
## Configuration
```python
# New environment variables
HTTP_ENABLED = 'MCP_HTTP_ENABLED'
HTTP_PORT = 'MCP_HTTP_PORT' (default: 8000)
HTTP_HOST = 'MCP_HTTP_HOST' (default: 0.0.0.0)
CORS_ORIGINS = 'MCP_CORS_ORIGINS'
SSE_HEARTBEAT_INTERVAL = 'MCP_SSE_HEARTBEAT' (default: 30s)
API_KEY = 'MCP_API_KEY' (optional auth)
```
## Performance Targets
- Memory storage: <50ms (vs ChromaDB ~500ms)
- Search response: <100ms for 1M memories
- SSE latency: <10ms from write to event
- Startup time: <1s (vs ChromaDB 5-10s)
## Testing Strategy
- Unit tests for all HTTP endpoints
- Integration tests for SSE connections
- Performance benchmarks vs ChromaDB
- Browser compatibility testing
- Edge deployment validation
## Security Considerations
- Optional API key authentication
- CORS configuration
- Rate limiting
- Input validation
- SSL/TLS documentation
## Migration Path
- Existing MCP users: No changes required
- ChromaDB users: Migration script provided
- New users: SQLite-vec as default for HTTP mode
## Benefits
- **Simplicity**: Single file database, no external services
- **Performance**: Orders of magnitude faster
- **Portability**: Runs anywhere Python runs
- **Reliability**: SQLite's proven track record
- **Modern**: HTTP/SSE/REST for web integration
- **Efficient**: Minimal resource usage
- **Edge-ready**: Deploy to CDN edge locations
## Future Possibilities
- Distributed SQLite with Litestream replication
- Cloudflare Workers deployment with D1
- Offline-first PWA with WASM SQLite
- Federation between multiple instances
## Success Metrics
- HTTP endpoints respond within performance targets
- SSE connections maintain real-time updates <10ms
- Dashboard provides intuitive memory management
- Documentation enables easy deployment
- Migration from ChromaDB is seamless
- Edge deployment works on major platforms
---
This plan represents a significant architectural improvement while maintaining full backward compatibility with existing MCP usage patterns.
```
--------------------------------------------------------------------------------
/docs/mastery/architecture-overview.md:
--------------------------------------------------------------------------------
```markdown
# MCP Memory Service — Architecture Overview
This document summarizes the Memory Service architecture, components, data flow, and how MCP integration is implemented.
## High-Level Design
- Clients: Claude Desktop/Code, VS Code, Cursor, Continue, and other MCP-compatible clients.
- Protocol Layer:
- MCP stdio server: `src/mcp_memory_service/server.py` (uses `mcp.server.Server`).
- FastAPI MCP server: `src/mcp_memory_service/mcp_server.py` (via `FastMCP`, exposes streamable HTTP for remote access).
- Core Domain:
- Models: `src/mcp_memory_service/models/memory.py` defining `Memory` and `MemoryQueryResult`.
- Utilities: hashing, time parsing, system detection, HTTP server coordination.
- Storage Abstraction:
- Interface: `src/mcp_memory_service/storage/base.py` (`MemoryStorage` ABC).
- Backends:
- SQLite-vec: `src/mcp_memory_service/storage/sqlite_vec.py` (recommended default).
- ChromaDB: `src/mcp_memory_service/storage/chroma.py` (deprecated; migration path provided).
- Cloudflare: `src/mcp_memory_service/storage/cloudflare.py` (Vectorize + D1 + optional R2).
- HTTP client: `src/mcp_memory_service/storage/http_client.py` (multi-client coordination).
- CLI:
- Entry points: `memory`, `memory-server`, `mcp-memory-server` (pyproject scripts).
- Implementation: `src/mcp_memory_service/cli/main.py` (server, status, ingestion commands).
- Config and Env:
- Central config: `src/mcp_memory_service/config.py` (paths, backend selection, HTTP/HTTPS, mDNS, consolidation, hostname tagging, Cloudflare settings).
- Consolidation (optional): associations, clustering, compression, forgetting; initialized lazily when enabled.
## Data Flow
1. Client invokes MCP tool/prompt (stdio or FastMCP HTTP transport).
2. Server resolves the configured backend via `config.py` and lazy/eager initializes storage.
3. For SQLite-vec:
- Embeddings generated via `sentence-transformers` (or ONNX disabled path) and stored alongside content and metadata in SQLite; vector search via `vec0` virtual table.
- WAL mode + busy timeouts for concurrent access; optional HTTP coordination for multi-client scenarios.
4. For ChromaDB: uses DuckDB+Parquet persistence and HNSW settings (deprecated path; migration messaging built-in).
5. For Cloudflare: Vectorize (vectors), D1 (metadata), R2 (large content); HTTPx for API calls.
6. Results map back to `Memory`/`MemoryQueryResult` and are returned to the MCP client.
## MCP Integration Patterns
- Stdio MCP (`server.py`):
- Uses `mcp.server.Server` and registers tools/prompts for memory operations, diagnostics, and analysis.
- Client-aware logging (`DualStreamHandler`) to keep JSON wire clean for Claude Desktop; richer stdout for LM Studio.
- Coordination: detects if an HTTP sidecar is needed for multi-client access; starts/uses `HTTPClientStorage` when appropriate.
- FastMCP (`mcp_server.py`):
- Wraps storage via `lifespan` context; exposes core tools like `store_memory`, `retrieve_memory`, `search_by_tag`, `delete_memory`, `check_database_health` using `@mcp.tool()`.
- Designed for remote/HTTP access and Claude Code compatibility via `streamable-http` transport.
## Storage Layer Abstraction
- `MemoryStorage` interface defines: `initialize`, `store`, `retrieve/search`, `search_by_tag(s)`, `delete`, `delete_by_tag`, `cleanup_duplicates`, `update_memory_metadata`, `get_stats`, plus optional helpers for tags/time ranges.
- Backends adhere to the interface and can be swapped via `MCP_MEMORY_STORAGE_BACKEND`.
## Configuration Management
- Paths: base dir and per-backend storage paths (auto-created, validated for writability).
- Backend selection: `MCP_MEMORY_STORAGE_BACKEND` ∈ `{sqlite_vec, chroma, cloudflare}` (normalized).
- HTTP/HTTPS server, CORS, API key, SSE heartbeat.
- mDNS discovery toggles and timeouts.
- Consolidation: enabled flag, archive path, decay/association/clustering/compression/forgetting knobs; schedules for APScheduler.
- Hostname tagging: `MCP_MEMORY_INCLUDE_HOSTNAME` annotates source host.
- Cloudflare: tokens, account, Vectorize index, D1 DB, optional R2, retry behavior.
## Dependencies and Roles
- `mcp`: MCP protocol server/runtime.
- `sqlite-vec`: vector index for SQLite; provides `vec0` virtual table.
- `sentence-transformers`, `torch`: embedding generation; can be disabled.
- `chromadb`: legacy backend (DuckDB+Parquet).
- `fastapi`, `uvicorn`, `sse-starlette`, `aiofiles`, `aiohttp/httpx`: HTTP transports and Cloudflare/API.
- `psutil`, `zeroconf`: client detection and mDNS discovery.
## Logging and Diagnostics
- Client-aware logging handler prevents stdout noise for Claude (keeps JSON clean) and surfaces info on LM Studio.
- `LOG_LEVEL` env to set root logger (defaults WARNING). Performance-critical third-party loggers elevated to WARNING unless `DEBUG_MODE` set.
## Performance and Concurrency
- SQLite-vec pragmas: WAL, busy_timeout, synchronous=NORMAL, cache_size, temp_store.
- Custom pragmas via `MCP_MEMORY_SQLITE_PRAGMAS`.
- Embedding model is cached and loaded once; ONNX path available when enabled.
- Average query time tracking, async operations, and optional consolidation scheduler.
```
--------------------------------------------------------------------------------
/archive/release-notes/release-notes-v7.1.4.md:
--------------------------------------------------------------------------------
```markdown
# MCP Memory Service v7.1.4 - Unified Cross-Platform Hook Installer
## 🚀 **Major Feature Release**
This release introduces a **unified cross-platform Python installer** that consolidates 4+ separate installer scripts into a single, robust solution with enhanced features for Claude Code Memory Awareness Hooks.
## ✨ **What's New**
### 🔧 **Unified Installation Experience**
- **Single installer**: `install_hooks.py` replaces all platform-specific scripts
- **Cross-platform compatibility**: Works seamlessly on Windows, macOS, and Linux
- **Intelligent configuration merging**: Preserves existing Claude Code hook configurations
- **Dynamic path resolution**: Eliminates hardcoded paths and works in any location
### 🎯 **Enhanced Safety & Reliability**
- **Atomic installations**: Automatic rollback on failure
- **Comprehensive backups**: Timestamped restore points before changes
- **Smart JSON merging**: Prevents settings.json overwrite and configuration loss
- **Empty directory cleanup**: Proper uninstall process with orphaned folder removal
### ⚡ **Natural Memory Triggers v7.1.3**
- **Advanced trigger detection**: 85%+ accuracy for intelligent memory injection
- **Multi-tier performance**: Optimized response times (50ms/150ms/500ms)
- **Mid-conversation hooks**: Real-time memory awareness during conversations
- **CLI management tools**: Live configuration and performance tuning
- **Git-aware context**: Repository integration for enhanced context
## 📋 **Installation Commands**
### New Unified Installation
```bash
# Navigate to hooks directory
cd claude-hooks
# Install Natural Memory Triggers (recommended)
python install_hooks.py --natural-triggers
# Install basic memory awareness hooks
python install_hooks.py --basic
# Install everything
python install_hooks.py --all
# Test installation (dry-run)
python install_hooks.py --dry-run --natural-triggers
```
### Integrated with Main Installer
```bash
# Install service + hooks together
python scripts/installation/install.py --install-natural-triggers
```
## 🔄 **Migration Guide**
### For Existing Users
1. **Backup existing installation** (automatic during upgrade)
2. **Run unified installer**: `python install_hooks.py --natural-triggers`
3. **Verify functionality**: Hooks preserve existing configurations
### From Legacy Scripts
- ❌ `install.sh` → ✅ `python install_hooks.py --basic`
- ❌ `install-natural-triggers.sh` → ✅ `python install_hooks.py --natural-triggers`
- ❌ `install_claude_hooks_windows.bat` → ✅ `python install_hooks.py --all`
**Complete migration guide**: See `claude-hooks/MIGRATION.md`
## 🛠 **Technical Improvements**
### Cross-Platform Enhancements
- **Proper path quoting**: Handles spaces in Windows installation paths
- **Platform-specific hooks directory detection**: Works across different OS configurations
- **Consistent CLI interface**: Same commands work on all platforms
### Code Quality
- **Type hints throughout**: Better maintainability and IDE support
- **Comprehensive error handling**: Graceful degradation and detailed feedback
- **Modular architecture**: Clear separation of concerns and extensibility
- **Professional UX**: Enhanced output formatting and user guidance
## ⚠️ **Breaking Changes**
- **Legacy shell scripts removed**: `install.sh`, `install-natural-triggers.sh`, `install_claude_hooks_windows.bat`
- **Installation commands updated**: Must use unified Python installer
- **Configuration structure**: Enhanced v7.1.3 dual protocol support
## 🧪 **Testing & Validation**
### Comprehensive Test Results
- **Natural Memory Triggers**: 18/18 tests passing (100% success rate)
- **Cross-platform compatibility**: Validated on Linux, macOS simulation, Windows paths
- **Installation integrity**: All components verified with syntax validation
- **Configuration merging**: Tested with various existing setups
### Performance Metrics
- **Installation time**: ~30 seconds for complete Natural Memory Triggers setup
- **Average test execution**: 3.3ms per test
- **Memory footprint**: Minimal impact with intelligent caching
## 🎯 **Benefits Summary**
This unified installer provides:
- ✅ **Better reliability** across all platforms
- ✅ **Safer installations** with intelligent configuration merging
- ✅ **Consistent experience** regardless of operating system
- ✅ **Advanced features** like Natural Memory Triggers v7.1.3
- ✅ **Professional tooling** with comprehensive testing and validation
- ✅ **Future-proof architecture** with extensible Python design
## 📞 **Support & Documentation**
- **Installation Guide**: `claude-hooks/MIGRATION.md`
- **Troubleshooting**: Run with `--dry-run` flag to diagnose issues
- **CLI Help**: `python install_hooks.py --help`
- **Issues**: [GitHub Issues](https://github.com/doobidoo/mcp-memory-service/issues)
## 🙏 **Acknowledgments**
Special thanks to **Gemini Code Assist** for comprehensive code review feedback that drove the safety and reliability improvements in this release.
---
This release represents a significant milestone in the evolution of Claude Code Memory Awareness Hooks, providing a unified, cross-platform installation experience with enhanced safety and advanced features.
```
--------------------------------------------------------------------------------
/archive/docs-root-cleanup-2025-08-23/DOCUMENTATION_ANALYSIS.md:
--------------------------------------------------------------------------------
```markdown
# MCP Memory Service - Documentation Analysis & Consolidation Plan
**Analysis Date**: 2025-08-23
**Total Files**: 87 markdown files (75 in `/docs/`, 12 in root)
**Total Size**: ~1MB of documentation
## 🚨 **Critical Redundancy Areas**
### 1. Installation Guides (MASSIVE OVERLAP)
**6+ files covering nearly identical installation steps:**
- **docs/guides/service-installation.md** (10KB) - Cross-platform service installation
- **docs/installation/complete-setup-guide.md** (7.7KB) - Complete setup with consolidation features
- **docs/installation/master-guide.md** (5KB) - Hardware-specific installation paths
- **docs/installation/distributed-sync.md** (11KB) - Installation + sync setup
- **docs/guides/claude-desktop-setup.md** (3.4KB) - Claude Desktop specific setup
- **README.md** (56KB) - Contains full installation instructions + everything else
**Redundancy**: Same basic steps (clone → install → configure) repeated 6 times with slight variations
### 2. Platform-Specific Setup (DUPLICATE CONTENT)
**4+ files with overlapping platform instructions:**
- **docs/platforms/windows.md** (11KB) - Windows setup
- **docs/guides/windows-setup.md** (3.9KB) - Windows setup (shorter version)
- **docs/platforms/ubuntu.md** (12.8KB) - Linux setup
- **docs/guides/UBUNTU_SETUP.md** (5.9KB) - Linux setup (different approach)
- **docs/platforms/macos-intel.md** (9.8KB) - macOS Intel setup
**Redundancy**: Platform-specific steps repeated across different file structures
### 3. Claude Integration (SCATTERED APPROACH)
**4+ files covering Claude integration:**
- **docs/guides/claude-code-integration.md** (10.6KB) - Claude Code integration
- **docs/guides/claude-code-quickstart.md** (3.9KB) - Quick start version
- **docs/guides/claude-desktop-setup.md** (3.4KB) - Desktop setup
- **docs/guides/claude_integration.md** (2.5KB) - Basic integration
- **docs/guides/claude-code-compatibility.md** (3.8KB) - Compatibility guide
**Redundancy**: Same configuration steps and JSON examples repeated
### 4. Development/Session Files (SHOULD BE ARCHIVED)
**10+ development artifacts mixed with user docs:**
- **docs/sessions/MCP_ENHANCEMENT_SESSION_MEMORY_v4.1.0.md** (12KB) - Development session
- **docs/development/** (multiple CLEANUP_*, TIMESTAMP_*, etc. files)
- **SESSION_MEMORY_2025-08-11.md** (4.6KB) - Personal session notes
- **CLAUDE_PERSONALIZED.md** (10.6KB) - Personal notes
**Issue**: Development artifacts shouldn't be in user-facing documentation
## 📊 **File Categories Analysis**
### **KEEP in Repository (4 files max)**
- **README.md** - Streamlined overview + wiki links
- **CLAUDE.md** - Claude Code development guidance
- **CHANGELOG.md** - Version history
- **CONTRIBUTING.md** - Development guidelines (if exists)
### **MOVE TO WIKI - Installation** (consolidate 6→1)
- All installation guides → Single comprehensive installation wiki page
- Platform-specific details → Sub-sections in installation page
### **MOVE TO WIKI - Integration** (consolidate 5→1)
- All Claude integration guides → Single integration wiki page
- Other IDE integrations → Sub-sections
### **MOVE TO WIKI - Technical** (organize existing)
- API documentation → Technical reference section
- Architecture docs → System design section
- Troubleshooting → Dedicated troubleshooting section
### **ARCHIVE/DELETE** (20+ files)
- All development session files
- Cleanup summaries and development artifacts
- Duplicate/outdated guides
- Personal session memories
## 🎯 **Consolidation Targets**
### **Target 1: Single Installation Guide**
**From**: 6 redundant installation files
**To**: 1 comprehensive wiki page with sections:
- Quick start (universal installer)
- Platform-specific notes (Windows/macOS/Linux)
- Hardware optimization (legacy vs modern)
- Service installation options
- Troubleshooting common issues
### **Target 2: Single Integration Guide**
**From**: 5 Claude integration files
**To**: 1 comprehensive integration page with:
- Claude Desktop setup
- Claude Code integration
- VS Code extension setup
- Other IDE configurations
- Configuration examples
### **Target 3: Technical Reference**
**From**: Scattered API/technical docs
**To**: Organized technical section:
- API documentation
- Architecture overview
- Storage backends comparison
- Performance optimization
- Development guidelines
## 📈 **Expected Results**
**Before**: 87 markdown files, difficult to navigate, redundant content
**After**: ~4 essential repo files + organized wiki with ~15 comprehensive pages
**Benefits**:
- **90% reduction** in documentation files in repository
- **Eliminated redundancy** - single source of truth for each topic
- **Improved discoverability** - logical wiki structure vs scattered files
- **Easier maintenance** - update once vs updating 6 installation guides
- **Cleaner repository** - focus on code, not documentation chaos
- **Better user experience** - clear paths vs overwhelming choice paralysis
## ✅ **Next Steps**
1. **Create wiki structure** with consolidated pages
2. **Migrate and merge content** from redundant files
3. **Update README.md** to point to wiki
4. **Remove redundant documentation** from repository
5. **Archive development artifacts** to separate location
```
--------------------------------------------------------------------------------
/scripts/testing/test_migration.py:
--------------------------------------------------------------------------------
```python
#!/usr/bin/env python3
# Copyright 2024 Heinrich Krupp
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Test script to verify ChromaDB to SQLite-vec migration.
This script compares data between ChromaDB and SQLite-vec to ensure
the migration was successful.
"""
import asyncio
import sys
from pathlib import Path
from typing import List, Dict, Any
# Add project root to path
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root / "src"))
from mcp_memory_service.storage.chroma import ChromaMemoryStorage
from mcp_memory_service.storage.sqlite_vec import SqliteVecMemoryStorage
from mcp_memory_service.config import CHROMA_PATH, EMBEDDING_MODEL_NAME
async def compare_storage_backends(chroma_path: str, sqlite_path: str):
"""Compare data between ChromaDB and SQLite-vec backends."""
print("🔍 Testing Migration Results")
print("=" * 50)
# Initialize both storages
chroma_storage = ChromaMemoryStorage(
persist_directory=chroma_path,
embedding_model_name=EMBEDDING_MODEL_NAME
)
await chroma_storage.initialize()
sqlite_storage = SqliteVecMemoryStorage(
db_path=sqlite_path,
embedding_model=EMBEDDING_MODEL_NAME
)
await sqlite_storage.initialize()
try:
# Get all memories from both storages
print("📥 Fetching memories from ChromaDB...")
chroma_memories = await chroma_storage.retrieve("", n_results=10000)
print("📥 Fetching memories from SQLite-vec...")
sqlite_memories = await sqlite_storage.retrieve("", n_results=10000)
# Compare counts
chroma_count = len(chroma_memories)
sqlite_count = len(sqlite_memories)
print(f"📊 ChromaDB memories: {chroma_count}")
print(f"📊 SQLite-vec memories: {sqlite_count}")
if sqlite_count >= chroma_count:
print("✅ Memory count check: PASSED")
else:
print("❌ Memory count check: FAILED")
print(f" Missing {chroma_count - sqlite_count} memories")
# Compare content hashes
chroma_hashes = {m.memory.content_hash for m in chroma_memories}
sqlite_hashes = {m.memory.content_hash for m in sqlite_memories}
missing_in_sqlite = chroma_hashes - sqlite_hashes
extra_in_sqlite = sqlite_hashes - chroma_hashes
if not missing_in_sqlite:
print("✅ Content hash check: PASSED")
else:
print("❌ Content hash check: FAILED")
print(f" {len(missing_in_sqlite)} memories missing in SQLite-vec")
if len(missing_in_sqlite) <= 5:
for hash_val in list(missing_in_sqlite)[:5]:
print(f" - {hash_val[:12]}...")
if extra_in_sqlite:
print(f"ℹ️ SQLite-vec has {len(extra_in_sqlite)} additional memories")
# Test search functionality
print("\\n🔍 Testing search functionality...")
if chroma_memories:
# Use first memory's content as search query
test_query = chroma_memories[0].memory.content[:50]
chroma_results = await chroma_storage.retrieve(test_query, n_results=5)
sqlite_results = await sqlite_storage.retrieve(test_query, n_results=5)
print(f"📊 Search results - ChromaDB: {len(chroma_results)}, SQLite-vec: {len(sqlite_results)}")
if len(sqlite_results) > 0:
print("✅ Search functionality: WORKING")
else:
print("❌ Search functionality: FAILED")
print("\\n🎉 Migration test completed!")
finally:
await chroma_storage.close()
await sqlite_storage.close()
async def main():
"""Main test function."""
import os
# Default paths
chroma_path = CHROMA_PATH
sqlite_path = os.path.join(os.path.dirname(chroma_path), 'memory_migrated.db')
# Allow custom paths via command line
if len(sys.argv) > 1:
sqlite_path = sys.argv[1]
print(f"📂 ChromaDB path: {chroma_path}")
print(f"📂 SQLite-vec path: {sqlite_path}")
print()
# Check if files exist
if not os.path.exists(chroma_path):
print(f"❌ ChromaDB not found at: {chroma_path}")
return 1
if not os.path.exists(sqlite_path):
print(f"❌ SQLite-vec database not found at: {sqlite_path}")
print("💡 Run the migration script first: python scripts/migrate_chroma_to_sqlite.py")
return 1
await compare_storage_backends(chroma_path, sqlite_path)
return 0
if __name__ == "__main__":
sys.exit(asyncio.run(main()))
```
--------------------------------------------------------------------------------
/scripts/maintenance/find_cloudflare_duplicates.py:
--------------------------------------------------------------------------------
```python
#!/usr/bin/env python3
"""Find all near-duplicate memories in Cloudflare D1 database."""
import asyncio
import os
import sys
from pathlib import Path
from collections import defaultdict
import hashlib
import re
async def main():
# Set OAuth to false to avoid validation issues
os.environ['MCP_OAUTH_ENABLED'] = 'false'
# Import after setting environment
from mcp_memory_service.storage.cloudflare import CloudflareStorage
from mcp_memory_service.config import (
CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID,
CLOUDFLARE_VECTORIZE_INDEX, CLOUDFLARE_D1_DATABASE_ID,
EMBEDDING_MODEL_NAME
)
def normalize_content(content):
"""Normalize content by removing timestamps and session-specific data."""
# Remove common timestamp patterns
normalized = content
normalized = re.sub(r'\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z', 'TIMESTAMP', normalized)
normalized = re.sub(r'\*\*Date\*\*: \d{2,4}[./]\d{2}[./]\d{2,4}', '**Date**: DATE', normalized)
normalized = re.sub(r'Timestamp: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}', 'Timestamp: TIMESTAMP', normalized)
return normalized.strip()
def content_hash(content):
"""Create a hash of normalized content."""
normalized = normalize_content(content)
return hashlib.md5(normalized.encode()).hexdigest()
print("🔗 Connecting to Cloudflare...")
# Initialize Cloudflare storage
cloudflare = CloudflareStorage(
api_token=CLOUDFLARE_API_TOKEN,
account_id=CLOUDFLARE_ACCOUNT_ID,
vectorize_index=CLOUDFLARE_VECTORIZE_INDEX,
d1_database_id=CLOUDFLARE_D1_DATABASE_ID,
embedding_model=EMBEDDING_MODEL_NAME
)
await cloudflare.initialize()
print("✅ Connected to Cloudflare\n")
print("📊 Fetching all memories from Cloudflare D1...")
# Use the public API method for better encapsulation and performance
try:
all_memories = await cloudflare.get_all_memories_bulk(include_tags=False)
except Exception as e:
print(f"❌ Failed to fetch memories from Cloudflare D1: {e}")
return 1
if not all_memories:
print("✅ No memories found to check for duplicates.")
return 0
# Convert Memory objects to the expected format for the rest of the script
memories = []
for memory in all_memories:
memories.append({
'content_hash': memory.content_hash,
'content': memory.content,
'tags': ','.join(memory.tags), # Convert list to comma-separated string
'created_at': memory.created_at
})
print(f"Total memories in Cloudflare: {len(memories)}\n")
# Group by normalized content
content_groups = defaultdict(list)
for mem in memories:
norm_hash = content_hash(mem['content'])
content_groups[norm_hash].append({
'hash': mem['content_hash'],
'content': mem['content'][:200], # First 200 chars
'tags': mem['tags'][:80] if mem['tags'] else '',
'created_at': mem['created_at']
})
# Find duplicates (groups with >1 memory)
duplicates = {k: v for k, v in content_groups.items() if len(v) > 1}
if not duplicates:
print("✅ No duplicates found in Cloudflare!")
return 0
print(f"\n❌ Found {len(duplicates)} groups of duplicates:\n")
total_duplicate_count = 0
for i, (norm_hash, group) in enumerate(duplicates.items(), 1):
count = len(group)
total_duplicate_count += count - 1 # Keep one, delete rest
print(f"{i}. Group with {count} duplicates:")
print(f" Content preview: {group[0]['content'][:100]}...")
print(f" Tags: {group[0]['tags'][:80]}...")
print(f" Hashes to keep: {group[0]['hash'][:16]}... (newest)")
print(f" Hashes to delete: {count-1} older duplicates")
if i >= 10: # Show only first 10 groups
remaining = len(duplicates) - 10
print(f"\n... and {remaining} more duplicate groups")
break
print(f"\n📊 Summary:")
print(f" Total duplicate groups: {len(duplicates)}")
print(f" Total memories to delete: {total_duplicate_count}")
print(f" Total memories after cleanup: {len(memories) - total_duplicate_count}")
# Ask if user wants to save hashes for deletion
save_hashes = input("\n💾 Save duplicate hashes for deletion? (y/n): ").strip().lower()
if save_hashes == 'y':
hash_file = Path.home() / "cloudflare_duplicates.txt"
# Collect hashes to delete (keep newest, delete older)
hashes_to_delete = []
for group in duplicates.values():
for memory in group[1:]: # Keep first (newest), delete rest
hashes_to_delete.append(memory['hash'])
with open(hash_file, 'w') as f:
for content_hash in hashes_to_delete:
f.write(f"{content_hash}\n")
print(f"\n✅ Saved {len(hashes_to_delete)} hashes to {hash_file}")
print(f"📋 Next step: Delete from Cloudflare")
print(f" Update delete_cloudflare_duplicates.py to read from cloudflare_duplicates.txt")
return 0
if __name__ == "__main__":
sys.exit(asyncio.run(main()))
```
--------------------------------------------------------------------------------
/scripts/utils/groq_agent_bridge.py:
--------------------------------------------------------------------------------
```python
#!/usr/bin/env python3
"""
Non-interactive Groq API client for AI agent integration.
Allows one AI system to call Groq's language models programmatically.
"""
import json
import os
import sys
from groq import Groq
from groq import APIError, AuthenticationError, RateLimitError, APIConnectionError
class GroqAgentBridge:
"""Bridge for other AI agents to call Groq's language models."""
def __init__(self, api_key=None):
"""Initialize with API key from environment or parameter."""
self.api_key = api_key or os.environ.get('GROQ_API_KEY')
if not self.api_key:
raise ValueError("GROQ_API_KEY environment variable required")
self.client = Groq(api_key=self.api_key)
def call_model(self, prompt, model="llama-3.3-70b-versatile",
max_tokens=1024, temperature=0.7, system_message=None):
"""
Non-interactive call to Groq's language model.
Args:
prompt: User prompt to send to the model
model: Model to use (default: llama-3.3-70b-versatile)
max_tokens: Maximum tokens in response
temperature: Sampling temperature
system_message: Optional system context message
Returns:
Dict with response data or error
"""
try:
messages = []
if system_message:
messages.append({"role": "system", "content": system_message})
messages.append({"role": "user", "content": prompt})
response = self.client.chat.completions.create(
model=model,
messages=messages,
max_tokens=max_tokens,
temperature=temperature
)
return {
"status": "success",
"response": response.choices[0].message.content,
"model": model,
"tokens_used": response.usage.total_tokens
}
except AuthenticationError as e:
return {
"status": "error",
"error": f"Authentication failed: {str(e)}. Check GROQ_API_KEY environment variable.",
"error_type": "authentication",
"model": model
}
except RateLimitError as e:
return {
"status": "error",
"error": f"Rate limit exceeded: {str(e)}. Please try again later.",
"error_type": "rate_limit",
"model": model
}
except APIConnectionError as e:
return {
"status": "error",
"error": f"Network connection failed: {str(e)}. Check your internet connection.",
"error_type": "connection",
"model": model
}
except APIError as e:
return {
"status": "error",
"error": f"Groq API error: {str(e)}",
"error_type": "api_error",
"model": model
}
except Exception as e:
# Catch-all for unexpected errors
return {
"status": "error",
"error": f"Unexpected error: {str(e)}",
"error_type": "unknown",
"model": model
}
def call_model_raw(self, prompt, **kwargs):
"""Raw text response for direct consumption by other agents."""
result = self.call_model(prompt, **kwargs)
if result["status"] == "success":
return result["response"]
else:
raise Exception(f"Groq API error: {result['error']}")
def main():
"""Command-line interface for non-interactive usage."""
import argparse
parser = argparse.ArgumentParser(description='Groq API Bridge for AI Agents')
parser.add_argument('prompt', help='Input prompt for the model')
parser.add_argument('--model', default='llama-3.3-70b-versatile',
help='Model to use (default: llama-3.3-70b-versatile)')
parser.add_argument('--max-tokens', type=int, default=1024,
help='Maximum tokens in response')
parser.add_argument('--temperature', type=float, default=0.7,
help='Sampling temperature')
parser.add_argument('--system', help='System message for context')
parser.add_argument('--json', action='store_true',
help='Output JSON response')
args = parser.parse_args()
try:
bridge = GroqAgentBridge()
result = bridge.call_model(
prompt=args.prompt,
model=args.model,
max_tokens=args.max_tokens,
temperature=args.temperature,
system_message=args.system
)
if args.json:
print(json.dumps(result, indent=2))
else:
if result["status"] == "success":
print(result["response"])
else:
print(f"Error: {result['error']}", file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
```
--------------------------------------------------------------------------------
/scripts/migration/verify_mcp_timestamps.py:
--------------------------------------------------------------------------------
```python
#!/usr/bin/env python3
"""
Verification script to check timestamp consistency in MCP Memory ChromaDB database.
Run this before and after migration to see the state of timestamps.
"""
import sqlite3
from datetime import datetime
import os
DB_PATH = "/Users/hkr/Library/Application Support/mcp-memory/chroma_db/chroma.sqlite3"
def check_timestamps():
"""Check current timestamp situation in the database."""
if not os.path.exists(DB_PATH):
print(f"❌ Database not found at: {DB_PATH}")
return
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
print("=" * 70)
print("MCP Memory Timestamp Verification Report")
print("=" * 70)
print(f"Database: {DB_PATH}")
print(f"Report generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
# 1. Count total memories
cursor.execute("SELECT COUNT(*) FROM embeddings")
total_memories = cursor.fetchone()[0]
print(f"Total memories in database: {total_memories}")
# 2. Check timestamp field distribution
print("\n📊 Timestamp Field Analysis:")
print("-" * 70)
cursor.execute("""
SELECT
key,
COUNT(DISTINCT id) as memories,
COUNT(CASE WHEN string_value IS NOT NULL THEN 1 END) as str_vals,
COUNT(CASE WHEN int_value IS NOT NULL THEN 1 END) as int_vals,
COUNT(CASE WHEN float_value IS NOT NULL THEN 1 END) as float_vals
FROM embedding_metadata
WHERE key IN ('timestamp', 'created_at', 'created_at_iso', 'timestamp_float',
'timestamp_str', 'updated_at', 'updated_at_iso', 'date')
GROUP BY key
ORDER BY memories DESC
""")
results = cursor.fetchall()
print(f"{'Field':<20} {'Memories':<12} {'String':<10} {'Int':<10} {'Float':<10}")
print("-" * 70)
for row in results:
print(f"{row[0]:<20} {row[1]:<12} {row[2]:<10} {row[3]:<10} {row[4]:<10}")
# 3. Check for memories without timestamps
print("\n📍 Missing Timestamp Analysis:")
cursor.execute("""
SELECT COUNT(DISTINCT e.id)
FROM embeddings e
WHERE e.id NOT IN (
SELECT id FROM embedding_metadata
WHERE key = 'timestamp' AND int_value IS NOT NULL
)
""")
missing_timestamps = cursor.fetchone()[0]
print(f"Memories without 'timestamp' field: {missing_timestamps}")
# 4. Show sample of different timestamp formats
print("\n📅 Sample Timestamp Values:")
print("-" * 70)
# Get a sample memory with multiple timestamp formats
cursor.execute("""
SELECT
em.id,
MAX(CASE WHEN em.key = 'timestamp' THEN em.int_value END) as ts_int,
MAX(CASE WHEN em.key = 'created_at' THEN em.float_value END) as created_float,
MAX(CASE WHEN em.key = 'timestamp_str' THEN em.string_value END) as ts_str,
SUBSTR(MAX(CASE WHEN em.key = 'chroma:document' THEN em.string_value END), 1, 50) as content
FROM embedding_metadata em
WHERE em.id IN (
SELECT DISTINCT id FROM embedding_metadata
WHERE key IN ('timestamp', 'created_at', 'timestamp_str')
)
GROUP BY em.id
HAVING COUNT(DISTINCT em.key) > 1
LIMIT 3
""")
samples = cursor.fetchall()
for i, (mem_id, ts_int, created_float, ts_str, content) in enumerate(samples, 1):
print(f"\nMemory ID {mem_id}:")
print(f" Content: {content}...")
if ts_int:
print(f" timestamp (int): {ts_int} = {datetime.fromtimestamp(ts_int).strftime('%Y-%m-%d %H:%M:%S')}")
if created_float:
print(f" created_at (float): {created_float} = {datetime.fromtimestamp(created_float).strftime('%Y-%m-%d %H:%M:%S.%f')[:23]}")
if ts_str:
print(f" timestamp_str: {ts_str}")
# 5. Date range analysis
print("\n📆 Timestamp Date Ranges:")
print("-" * 70)
# For each timestamp field, show the date range
for field, dtype in [('timestamp', 'int_value'), ('created_at', 'float_value')]:
cursor.execute(f"""
SELECT
MIN({dtype}) as min_val,
MAX({dtype}) as max_val,
COUNT(DISTINCT id) as count
FROM embedding_metadata
WHERE key = ? AND {dtype} IS NOT NULL
""", (field,))
result = cursor.fetchone()
if result and result[2] > 0:
min_date = datetime.fromtimestamp(result[0]).strftime('%Y-%m-%d')
max_date = datetime.fromtimestamp(result[1]).strftime('%Y-%m-%d')
print(f"{field:<15} ({result[2]} memories): {min_date} to {max_date}")
# 6. Summary recommendation
print("\n💡 Recommendations:")
print("-" * 70)
if missing_timestamps > 0:
print(f"⚠️ {missing_timestamps} memories need timestamp migration")
if len(results) > 1:
print(f"⚠️ Found {len(results)} different timestamp fields - consolidation recommended")
print(" Run cleanup_mcp_timestamps.py to fix this issue")
else:
print("✅ Timestamp fields look clean!")
conn.close()
if __name__ == "__main__":
check_timestamps()
```
--------------------------------------------------------------------------------
/scripts/pr/amp_quality_gate.sh:
--------------------------------------------------------------------------------
```bash
#!/bin/bash
# scripts/pr/amp_quality_gate.sh - Parallel quality checks using Amp CLI
#
# Usage: bash scripts/pr/amp_quality_gate.sh <PR_NUMBER>
# Example: bash scripts/pr/amp_quality_gate.sh 215
# For local branch (pre-PR): bash scripts/pr/amp_quality_gate.sh 0
set -e
PR_NUMBER=$1
if [ -z "$PR_NUMBER" ]; then
echo "Usage: $0 <PR_NUMBER>"
echo "Use 0 for local branch (pre-PR checks)"
exit 1
fi
# Ensure Amp prompt directories exist
mkdir -p .claude/amp/prompts/pending
mkdir -p .claude/amp/responses/ready
echo "=== Amp CLI Quality Gate for PR #$PR_NUMBER ==="
echo ""
# Get changed Python files
if [ "$PR_NUMBER" = "0" ]; then
echo "Analyzing local branch changes..."
changed_files=$(git diff --name-only origin/main | grep '\.py$' || echo "")
else
if ! command -v gh &> /dev/null; then
echo "Error: GitHub CLI (gh) is not installed"
exit 1
fi
echo "Fetching changed files from PR #$PR_NUMBER..."
changed_files=$(gh pr diff $PR_NUMBER --name-only | grep '\.py$' || echo "")
fi
if [ -z "$changed_files" ]; then
echo "No Python files changed."
exit 0
fi
echo "Changed Python files:"
echo "$changed_files"
echo ""
# Generate UUIDs for each check
complexity_uuid=$(uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid)
security_uuid=$(uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid)
typehints_uuid=$(uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid)
# Store UUIDs for result collection
echo "$complexity_uuid,$security_uuid,$typehints_uuid" > /tmp/amp_quality_gate_uuids_${PR_NUMBER}.txt
echo "Creating Amp prompts for parallel processing..."
echo ""
# Create complexity check prompt
cat > .claude/amp/prompts/pending/complexity-${complexity_uuid}.json << EOF
{
"id": "${complexity_uuid}",
"timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")",
"prompt": "Analyze code complexity for each function in these files. Rating scale: 1-10 (1=simple, 10=very complex). ONLY report functions with score >7 in this exact format: 'File:Function: Score X - Reason'. If all functions score ≤7, respond: 'COMPLEXITY_OK'. Files:\n\n$(echo "$changed_files" | while read file; do echo "=== $file ==="; cat "$file" 2>/dev/null || echo "File not found"; echo ""; done)",
"context": {
"project": "mcp-memory-service",
"task": "complexity-analysis",
"pr_number": "${PR_NUMBER}"
},
"options": {
"timeout": 120000,
"format": "text"
}
}
EOF
# Create security scan prompt
cat > .claude/amp/prompts/pending/security-${security_uuid}.json << EOF
{
"id": "${security_uuid}",
"timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")",
"prompt": "Security audit for vulnerabilities: SQL injection (raw SQL, string formatting in queries), XSS (unescaped HTML output), command injection (os.system, subprocess with shell=True), path traversal (user input in file paths), hardcoded secrets (API keys, passwords). IMPORTANT: Output format - If ANY vulnerability found: 'VULNERABILITY_DETECTED: [type] - [details]'. If NO vulnerabilities: 'SECURITY_CLEAN'. Files:\n\n$(echo "$changed_files" | while read file; do echo "=== $file ==="; cat "$file" 2>/dev/null || echo "File not found"; echo ""; done)",
"context": {
"project": "mcp-memory-service",
"task": "security-scan",
"pr_number": "${PR_NUMBER}"
},
"options": {
"timeout": 120000,
"format": "text"
}
}
EOF
# Create type hints check prompt
cat > .claude/amp/prompts/pending/typehints-${typehints_uuid}.json << EOF
{
"id": "${typehints_uuid}",
"timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")",
"prompt": "Check type hint coverage for these Python files. Report: 1) Total functions/methods, 2) Functions with complete type hints, 3) Functions missing type hints (list names), 4) Coverage percentage. Output format: 'COVERAGE: X%' then 'MISSING: function1, function2, ...' (or 'NONE' if all covered). Files:\n\n$(echo "$changed_files" | while read file; do echo "=== $file ==="; cat "$file" 2>/dev/null || echo "File not found"; echo ""; done)",
"context": {
"project": "mcp-memory-service",
"task": "type-hints",
"pr_number": "${PR_NUMBER}"
},
"options": {
"timeout": 120000,
"format": "text"
}
}
EOF
echo "✅ Created 3 Amp prompts for parallel processing"
echo ""
echo "=== Run these Amp commands in parallel (in separate terminals or background) ==="
echo ""
echo "amp @.claude/amp/prompts/pending/complexity-${complexity_uuid}.json &"
echo "amp @.claude/amp/prompts/pending/security-${security_uuid}.json &"
echo "amp @.claude/amp/prompts/pending/typehints-${typehints_uuid}.json &"
echo ""
echo "=== Then collect results with ==="
echo "bash scripts/pr/amp_collect_results.sh --timeout 300 --uuids '${complexity_uuid},${security_uuid},${typehints_uuid}'"
echo ""
echo "=== Or use this one-liner to run all in background ==="
echo "(amp @.claude/amp/prompts/pending/complexity-${complexity_uuid}.json > /tmp/amp-complexity.log 2>&1 &); (amp @.claude/amp/prompts/pending/security-${security_uuid}.json > /tmp/amp-security.log 2>&1 &); (amp @.claude/amp/prompts/pending/typehints-${typehints_uuid}.json > /tmp/amp-typehints.log 2>&1 &); sleep 10 && bash scripts/pr/amp_collect_results.sh --timeout 300 --uuids '${complexity_uuid},${security_uuid},${typehints_uuid}'"
```
--------------------------------------------------------------------------------
/install_service.py:
--------------------------------------------------------------------------------
```python
#!/usr/bin/env python3
"""
Cross-platform service installer for MCP Memory Service.
Automatically detects the operating system and installs the appropriate service.
"""
import os
import sys
import platform
import argparse
import subprocess
from pathlib import Path
def print_header(text):
"""Print a formatted header."""
print("\n" + "=" * 60)
print(f" {text}")
print("=" * 60)
def print_error(text):
"""Print formatted error text."""
print(f"\n❌ ERROR: {text}")
def print_info(text):
"""Print formatted info text."""
print(f"ℹ️ {text}")
def detect_platform():
"""Detect the current platform."""
system = platform.system().lower()
platforms = {
'windows': 'Windows',
'darwin': 'macOS',
'linux': 'Linux'
}
return system, platforms.get(system, 'Unknown')
def check_python_version():
"""Check if Python version meets requirements."""
if sys.version_info < (3, 10):
print_error(f"Python 3.10 or newer is required. Found: {sys.version}")
sys.exit(1)
def run_platform_installer(platform_name, args):
"""Run the appropriate platform-specific installer."""
# Get the directory where this script is located
script_dir = Path(__file__).parent
scripts_dir = script_dir / 'scripts'
installers = {
'windows': scripts_dir / 'install_windows_service.py',
'darwin': scripts_dir / 'install_macos_service.py',
'linux': scripts_dir / 'install_linux_service.py'
}
installer = installers.get(platform_name)
if not installer:
print_error(f"No installer available for platform: {platform_name}")
sys.exit(1)
if not installer.exists():
print_error(f"Platform installer not found: {installer}")
print_info("This installer may not be implemented yet.")
# For Linux, fall back to the bash script if Python version doesn't exist
if platform_name == 'linux':
bash_installer = script_dir / 'install_service.sh'
if bash_installer.exists():
print_info("Falling back to bash installer for Linux...")
# Make sure the script is executable
bash_installer.chmod(0o755)
# Run the bash script
try:
subprocess.run([str(bash_installer)], check=True)
return
except subprocess.CalledProcessError as e:
print_error(f"Installation failed: {e}")
sys.exit(1)
sys.exit(1)
# Build command with arguments
cmd = [sys.executable, str(installer)]
# Pass through command-line arguments
if args.command:
cmd.extend(['--command', args.command])
if args.uninstall:
cmd.append('--uninstall')
if args.start:
cmd.append('--start')
if args.stop:
cmd.append('--stop')
if args.status:
cmd.append('--status')
if args.user:
cmd.append('--user')
if args.system:
cmd.append('--system')
# Run the platform-specific installer
try:
subprocess.run(cmd, check=True)
except subprocess.CalledProcessError as e:
print_error(f"Installation failed: {e}")
sys.exit(1)
except FileNotFoundError:
print_error(f"Could not run installer: {installer}")
sys.exit(1)
def main():
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Cross-platform service installer for MCP Memory Service"
)
# Service operations
parser.add_argument(
'--command',
choices=['install', 'uninstall', 'start', 'stop', 'restart', 'status'],
help='Service command to execute'
)
parser.add_argument('--uninstall', action='store_true', help='Uninstall the service')
parser.add_argument('--start', action='store_true', help='Start the service after installation')
parser.add_argument('--stop', action='store_true', help='Stop the service')
parser.add_argument('--status', action='store_true', help='Check service status')
# Installation options
parser.add_argument('--user', action='store_true', help='Install as user service (default)')
parser.add_argument('--system', action='store_true', help='Install as system service (requires admin)')
args = parser.parse_args()
# Print header
print_header("MCP Memory Service - Cross-Platform Installer")
# Check Python version
check_python_version()
# Detect platform
platform_name, platform_display = detect_platform()
print_info(f"Detected platform: {platform_display}")
print_info(f"Python version: {sys.version.split()[0]}")
# Check for virtual environment
if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix):
print_info(f"Virtual environment: {sys.prefix}")
else:
print_info("⚠️ Not running in a virtual environment (recommended)")
# Run platform-specific installer
print_info(f"Running {platform_display} service installer...")
run_platform_installer(platform_name, args)
if __name__ == '__main__':
main()
```
--------------------------------------------------------------------------------
/claude-hooks/tests/test-session-tracking.json:
--------------------------------------------------------------------------------
```json
{
"sessions": [
{
"id": "test-session-1759496420980",
"startTime": "2025-10-03T13:00:20.980Z",
"endTime": null,
"projectContext": {
"name": "mcp-memory-service",
"type": "Multi-language Project",
"languages": [
"javascript",
"python"
],
"frameworks": [
"node.js",
"fastapi"
],
"tools": [
"git",
"npm",
"pip"
],
"confidence": 0.95
},
"workingDirectory": "/test/directory",
"initialTopics": [],
"finalTopics": [],
"memoriesLoaded": [],
"memoriesCreated": [],
"conversationSummary": null,
"outcome": null,
"threadId": "thread-8e3c1ef4f0d194a7",
"parentSessionId": null,
"childSessionIds": [],
"status": "active"
}
],
"conversationThreads": [
{
"id": "thread-580819bbd8f0cd81",
"createdAt": "2025-08-20T11:38:33.001Z",
"projectContext": {
"name": "mcp-memory-service",
"type": "Multi-language Project",
"languages": [
"javascript",
"python"
],
"frameworks": [
"node.js",
"fastapi"
],
"tools": [
"git",
"npm",
"pip"
],
"confidence": 0.95
},
"sessionIds": [
"test-session-1755689913000"
],
"topics": [],
"outcomes": [],
"status": "active"
},
{
"id": "thread-e5d79cf384f81206",
"createdAt": "2025-08-20T11:42:10.896Z",
"projectContext": {
"name": "mcp-memory-service",
"type": "Multi-language Project",
"languages": [
"javascript",
"python"
],
"frameworks": [
"node.js",
"fastapi"
],
"tools": [
"git",
"npm",
"pip"
],
"confidence": 0.95
},
"sessionIds": [
"test-session-1755690130896"
],
"topics": [],
"outcomes": [],
"status": "active"
},
{
"id": "thread-baec3bef4586c544",
"createdAt": "2025-08-20T11:43:24.007Z",
"projectContext": {
"name": "mcp-memory-service",
"type": "Multi-language Project",
"languages": [
"javascript",
"python"
],
"frameworks": [
"node.js",
"fastapi"
],
"tools": [
"git",
"npm",
"pip"
],
"confidence": 0.95
},
"sessionIds": [
"test-session-1755690204007"
],
"topics": [],
"outcomes": [],
"status": "active"
},
{
"id": "thread-8ceebf438da3ede6",
"createdAt": "2025-08-20T11:43:49.796Z",
"projectContext": {
"name": "mcp-memory-service",
"type": "Multi-language Project",
"languages": [
"javascript",
"python"
],
"frameworks": [
"node.js",
"fastapi"
],
"tools": [
"git",
"npm",
"pip"
],
"confidence": 0.95
},
"sessionIds": [
"test-session-1755690229795"
],
"topics": [],
"outcomes": [],
"status": "active"
},
{
"id": "thread-3596674e63855259",
"createdAt": "2025-08-20T11:44:35.337Z",
"projectContext": {
"name": "mcp-memory-service",
"type": "Multi-language Project",
"languages": [
"javascript",
"python"
],
"frameworks": [
"node.js",
"fastapi"
],
"tools": [
"git",
"npm",
"pip"
],
"confidence": 0.95
},
"sessionIds": [
"test-session-1755690275336"
],
"topics": [],
"outcomes": [],
"status": "active"
},
{
"id": "thread-a1b7d615834f2c47",
"createdAt": "2025-08-20T11:45:38.589Z",
"projectContext": {
"name": "mcp-memory-service",
"type": "Multi-language Project",
"languages": [
"javascript",
"python"
],
"frameworks": [
"node.js",
"fastapi"
],
"tools": [
"git",
"npm",
"pip"
],
"confidence": 0.95
},
"sessionIds": [
"test-session-1755690338588"
],
"topics": [],
"outcomes": [],
"status": "active"
},
{
"id": "thread-8e3c1ef4f0d194a7",
"createdAt": "2025-10-03T13:00:20.980Z",
"projectContext": {
"name": "mcp-memory-service",
"type": "Multi-language Project",
"languages": [
"javascript",
"python"
],
"frameworks": [
"node.js",
"fastapi"
],
"tools": [
"git",
"npm",
"pip"
],
"confidence": 0.95
},
"sessionIds": [
"test-session-1759496420980"
],
"topics": [],
"outcomes": [],
"status": "active"
}
],
"projectSessions": {
"mcp-memory-service": [
"test-session-1759496420980"
]
},
"lastSaved": "2025-10-03T13:00:20.980Z"
}
```
--------------------------------------------------------------------------------
/.github/workflows/roadmap-review-reminder.yml:
--------------------------------------------------------------------------------
```yaml
name: Quarterly Roadmap Review Reminder
on:
schedule:
# Runs at 09:00 UTC on the 1st of Feb, May, Aug, Nov
- cron: '0 9 1 2,5,8,11 *'
workflow_dispatch: # Allow manual trigger for testing or ad-hoc reviews
jobs:
create-review-issue:
runs-on: ubuntu-latest
permissions:
issues: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Determine quarter
id: quarter
run: |
MONTH=$(date +%m)
YEAR=$(date +%Y)
case $MONTH in
02) QUARTER="Q1" ;;
05) QUARTER="Q2" ;;
08) QUARTER="Q3" ;;
11) QUARTER="Q4" ;;
*) QUARTER="Q?" ;;
esac
echo "quarter=$QUARTER" >> $GITHUB_OUTPUT
echo "year=$YEAR" >> $GITHUB_OUTPUT
echo "Detected: $QUARTER $YEAR"
- name: Create roadmap review issue
uses: actions/github-script@v7
with:
script: |
const quarter = '${{ steps.quarter.outputs.quarter }}';
const year = '${{ steps.quarter.outputs.year }}';
const issueTitle = `Quarterly Roadmap Review - ${quarter} ${year}`;
const issueBody = `## 📊 Quarterly Roadmap Review
It's time for the quarterly roadmap review! Please review and update the development roadmap on the wiki.
**📖 Wiki Roadmap**: [13-Development-Roadmap](https://github.com/doobidoo/mcp-memory-service/wiki/13-Development-Roadmap)
## ✅ Review Checklist
- [ ] **Completed Milestones**: Move finished features from "Current Focus" to "Completed Milestones"
- [ ] **Current Focus**: Update v8.39-v9.0 goals based on actual progress
- [ ] **Timeline Accuracy**: Verify Q1 2026 timeline is still realistic
- [ ] **Future Enhancements**: Adjust Q2 2026+ plans based on community feedback
- [ ] **Version References**: Update current version number in "Project Status" section
- [ ] **GitHub Projects Alignment**: Check if [GitHub Projects](https://github.com/doobidoo/mcp-memory-service/projects) need updates
- [ ] **Community Opportunities**: Review and update contribution opportunities section
- [ ] **Next Review Date**: Set next quarterly review date (3 months from now)
## 🔍 What to Check
**Recent Releases** (since last review):
- Check [CHANGELOG.md](../CHANGELOG.md) for completed features
- Review [Recent Commits](https://github.com/doobidoo/mcp-memory-service/commits/main) for major changes
- Check [Closed Issues](https://github.com/doobidoo/mcp-memory-service/issues?q=is%3Aissue+is%3Aclosed) for resolved items
**Community Feedback**:
- Review [Open Issues](https://github.com/doobidoo/mcp-memory-service/issues) for feature requests
- Check [Discussions](https://github.com/doobidoo/mcp-memory-service/discussions) for community input
- Consider [Pull Requests](https://github.com/doobidoo/mcp-memory-service/pulls) for emerging patterns
## 📝 Update Guidelines
**Why Wiki?**
- ✅ No PR required - edit directly for faster updates
- ✅ Better navigation - integrated with other wiki guides
- ✅ Community collaboration - lower barrier for community input
**Documentation Matrix**:
- **Wiki Roadmap**: Strategic vision, quarterly goals, long-term aspirations
- **GitHub Projects**: Sprint planning, task boards, issue tracking (if enabled)
- **CHANGELOG.md**: Release history, completed features
- **Open Issues**: Bug reports, feature requests, immediate priorities
## 🎯 Success Metrics
After review, the roadmap should:
- Accurately reflect current version and recent achievements
- Provide clear guidance for next quarter's priorities
- Inspire community contributions with well-defined opportunities
- Align strategic vision with tactical execution (GitHub Projects/Issues)
---
**Automated Reminder**: This issue is created quarterly by [roadmap-review-reminder workflow](../.github/workflows/roadmap-review-reminder.yml)
**Maintainer**: @doobidoo
**Due Date**: Within 2 weeks of creation`;
// Check if issue already exists for this quarter
const { data: existingIssues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'roadmap',
per_page: 100
});
const isDuplicate = existingIssues.some(issue =>
issue.title.includes(issueTitle)
);
if (isDuplicate) {
console.log(`✅ Issue already exists for ${quarter} ${year} - skipping creation`);
return;
}
// Create the issue
const { data: issue } = await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: issueTitle,
body: issueBody,
labels: ['documentation', 'maintenance', 'roadmap'],
assignees: ['doobidoo']
});
console.log(`✅ Created issue #${issue.number}: ${issueTitle}`);
```
--------------------------------------------------------------------------------
/scripts/sync/check_drift.py:
--------------------------------------------------------------------------------
```python
#!/usr/bin/env python3
"""
Check for metadata drift between hybrid backends (dry-run support).
This script checks for memories with divergent metadata (tags, types, custom fields)
between local SQLite-vec and Cloudflare backends, without making any changes.
Usage:
python scripts/sync/check_drift.py # Dry-run mode (preview only)
python scripts/sync/check_drift.py --apply # Apply changes
python scripts/sync/check_drift.py --limit 50 # Check 50 memories max
Requires:
- Hybrid storage backend configured
- MCP_HYBRID_SYNC_UPDATES=true (or enabled by default)
Output:
- Number of memories checked
- Number with metadata drift detected
- Number that would be synced (or were synced with --apply)
- Number of failures
Version: 8.25.0+
"""
import asyncio
import logging
import sys
import argparse
from pathlib import Path
# Add parent directory to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
from src.mcp_memory_service.storage.hybrid import HybridMemoryStorage
from src.mcp_memory_service import config as app_config
# Set up logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
async def main():
"""Run drift detection check."""
parser = argparse.ArgumentParser(
description="Check for metadata drift between hybrid backends"
)
parser.add_argument(
'--apply',
action='store_true',
help='Apply changes (default is dry-run mode)'
)
parser.add_argument(
'--limit',
type=int,
default=None,
help='Maximum number of memories to check (default: from config)'
)
parser.add_argument(
'--verbose',
'-v',
action='store_true',
help='Enable verbose debug logging'
)
args = parser.parse_args()
if args.verbose:
logging.getLogger().setLevel(logging.DEBUG)
# Check that hybrid backend is configured
if app_config.STORAGE_BACKEND != 'hybrid':
logger.error(f"Drift detection requires hybrid backend, but configured backend is: {app_config.STORAGE_BACKEND}")
logger.error("Set MCP_MEMORY_STORAGE_BACKEND=hybrid in your environment or .env file")
return 1
# Override batch size if limit specified
if args.limit:
app_config.HYBRID_DRIFT_BATCH_SIZE = args.limit
logger.info("=== Hybrid Backend Drift Detection ===")
logger.info(f"Mode: {'APPLY CHANGES' if args.apply else 'DRY RUN (preview only)'}")
logger.info(f"Batch size: {args.limit or app_config.HYBRID_DRIFT_BATCH_SIZE}")
logger.info(f"Drift detection enabled: {app_config.HYBRID_SYNC_UPDATES}")
if not app_config.HYBRID_SYNC_UPDATES:
logger.warning("Drift detection is disabled (MCP_HYBRID_SYNC_UPDATES=false)")
logger.warning("Set MCP_HYBRID_SYNC_UPDATES=true to enable this feature")
return 1
try:
# Initialize hybrid storage with db path and Cloudflare config
db_path = app_config.SQLITE_VEC_PATH
# Build Cloudflare config from environment
cloudflare_keys = [
'CLOUDFLARE_API_TOKEN',
'CLOUDFLARE_ACCOUNT_ID',
'CLOUDFLARE_D1_DATABASE_ID',
'CLOUDFLARE_VECTORIZE_INDEX',
'CLOUDFLARE_R2_BUCKET',
'CLOUDFLARE_EMBEDDING_MODEL',
'CLOUDFLARE_LARGE_CONTENT_THRESHOLD',
'CLOUDFLARE_MAX_RETRIES',
'CLOUDFLARE_BASE_DELAY',
]
cloudflare_config = {
key.lower().replace('cloudflare_', ''): getattr(app_config, key, None)
for key in cloudflare_keys
}
storage = HybridMemoryStorage(
sqlite_db_path=db_path,
cloudflare_config=cloudflare_config
)
await storage.initialize()
# Check that sync service is available
if not storage.sync_service:
logger.error("Sync service not available - hybrid backend may not be configured correctly")
return 1
logger.info(f"Sync service initialized (drift check interval: {storage.sync_service.drift_check_interval}s)")
# Run drift detection
logger.info("\nStarting drift detection scan...\n")
stats = await storage.sync_service._detect_and_sync_drift(dry_run=not args.apply)
# Print results
print("\n" + "="*60)
print(f"DRIFT DETECTION RESULTS {'(DRY RUN)' if not args.apply else '(CHANGES APPLIED)'}")
print("="*60)
print(f" Memories checked: {stats['checked']}")
print(f" Drift detected: {stats['drift_detected']}")
print(f" {'Would sync' if not args.apply else 'Synced'}: {stats['synced']}")
print(f" Failed: {stats['failed']}")
print("="*60)
if stats['drift_detected'] > 0 and not args.apply:
print("\nℹ️ Run with --apply to synchronize these memories")
elif stats['drift_detected'] > 0 and args.apply:
print("\n✅ Metadata synchronized successfully")
else:
print("\n✅ No drift detected - backends are in sync")
return 0
except Exception as e:
logger.error(f"Error during drift detection: {e}", exc_info=True)
return 1
if __name__ == '__main__':
exit_code = asyncio.run(main())
sys.exit(exit_code)
```
--------------------------------------------------------------------------------
/scripts/sync/litestream/push_to_remote.sh:
--------------------------------------------------------------------------------
```bash
#!/bin/bash
# Push staged changes to remote MCP Memory Service API
STAGING_DB="/Users/hkr/Library/Application Support/mcp-memory/sqlite_vec_staging.db"
REMOTE_API="https://narrowbox.local:8443/api/memories"
# API_KEY can be set as environment variable or read from config
API_KEY="${MCP_API_KEY:-}"
HOSTNAME=$(hostname)
echo "$(date): Pushing staged changes to remote API..."
if [ ! -f "$STAGING_DB" ]; then
echo "$(date): No staging database found - nothing to push"
exit 0
fi
# Check if we have API key (if required)
if [ -z "$API_KEY" ]; then
echo "$(date): WARNING: No API key configured. Set MCP_API_KEY environment variable if required"
fi
# Get count of changes ready to push
PUSH_COUNT=$(sqlite3 "$STAGING_DB" "
SELECT COUNT(*) FROM staged_memories
WHERE conflict_status = 'none'
AND operation IN ('INSERT', 'UPDATE');
" 2>/dev/null || echo "0")
if [ "$PUSH_COUNT" -eq 0 ]; then
echo "$(date): No changes ready to push"
exit 0
fi
echo "$(date): Found $PUSH_COUNT changes ready to push to remote API"
# Test connectivity to remote API
echo "$(date): Testing connectivity to remote API..."
HTTP_STATUS=$(curl -k -s -o /dev/null -w "%{http_code}" "$REMOTE_API" --connect-timeout 10)
if [ "$HTTP_STATUS" -eq 000 ]; then
echo "$(date): ERROR: Cannot connect to remote API at $REMOTE_API"
echo "$(date): Changes will remain staged for next attempt"
exit 1
elif [ "$HTTP_STATUS" -eq 404 ]; then
echo "$(date): WARNING: API endpoint not found (404). Checking if server is running..."
elif [ "$HTTP_STATUS" -ge 200 ] && [ "$HTTP_STATUS" -lt 300 ]; then
echo "$(date): API connectivity confirmed (HTTP $HTTP_STATUS)"
else
echo "$(date): WARNING: Unexpected HTTP status: $HTTP_STATUS"
fi
# Process each change ready for push
PUSHED_COUNT=0
FAILED_COUNT=0
sqlite3 "$STAGING_DB" "
SELECT id, content, content_hash, tags, metadata, memory_type,
operation, staged_at, original_created_at, source_machine
FROM staged_memories
WHERE conflict_status = 'none'
AND operation IN ('INSERT', 'UPDATE')
ORDER BY staged_at ASC;
" | while IFS='|' read -r id content content_hash tags metadata memory_type operation staged_at created_at source_machine; do
echo "$(date): Pushing: ${content:0:50}..."
# Prepare JSON payload
# Note: This assumes the API accepts the memory service format
JSON_PAYLOAD=$(cat << EOF
{
"content": $(echo "$content" | jq -R .),
"tags": $tags,
"metadata": $metadata,
"memory_type": "$memory_type",
"client_hostname": "$HOSTNAME"
}
EOF
)
# Prepare curl command with optional API key
CURL_CMD="curl -k -s -X POST"
CURL_CMD="$CURL_CMD -H 'Content-Type: application/json'"
CURL_CMD="$CURL_CMD -H 'X-Client-Hostname: $HOSTNAME'"
if [ -n "$API_KEY" ]; then
CURL_CMD="$CURL_CMD -H 'Authorization: Bearer $API_KEY'"
fi
CURL_CMD="$CURL_CMD -d '$JSON_PAYLOAD'"
CURL_CMD="$CURL_CMD '$REMOTE_API'"
# Execute push to remote API
RESPONSE=$(eval "$CURL_CMD" 2>&1)
CURL_EXIT_CODE=$?
if [ $CURL_EXIT_CODE -eq 0 ]; then
# Check if response indicates success
if echo "$RESPONSE" | grep -q '"success":\s*true\|"status":\s*"success"\|content_hash'; then
echo "$(date): Successfully pushed: ${content:0:30}..."
# Remove from staging on successful push
sqlite3 "$STAGING_DB" "DELETE FROM staged_memories WHERE id = '$id';"
PUSHED_COUNT=$((PUSHED_COUNT + 1))
elif echo "$RESPONSE" | grep -q '"error"\|"message"\|HTTP.*[45][0-9][0-9]'; then
echo "$(date): API error for: ${content:0:30}..."
echo "$(date): Response: $RESPONSE"
FAILED_COUNT=$((FAILED_COUNT + 1))
# Mark as failed but don't delete (for retry)
sqlite3 "$STAGING_DB" "
UPDATE staged_memories
SET conflict_status = 'push_failed'
WHERE id = '$id';
"
else
echo "$(date): Unexpected response: $RESPONSE"
FAILED_COUNT=$((FAILED_COUNT + 1))
fi
else
echo "$(date): Network error pushing: ${content:0:30}..."
echo "$(date): Error: $RESPONSE"
FAILED_COUNT=$((FAILED_COUNT + 1))
# Don't mark as failed if it's a network issue - keep for retry
fi
# Small delay to avoid overwhelming the API
sleep 0.5
done
# Get final counts
REMAINING_COUNT=$(sqlite3 "$STAGING_DB" "SELECT COUNT(*) FROM staged_memories WHERE conflict_status = 'none';" 2>/dev/null || echo "0")
FAILED_FINAL=$(sqlite3 "$STAGING_DB" "SELECT COUNT(*) FROM staged_memories WHERE conflict_status = 'push_failed';" 2>/dev/null || echo "0")
echo "$(date): Push operation completed"
echo "$(date): Successfully pushed: $PUSHED_COUNT changes"
echo "$(date): Failed to push: $FAILED_FINAL changes"
echo "$(date): Remaining staged: $REMAINING_COUNT changes"
# Update sync status
sqlite3 "$STAGING_DB" "
INSERT OR REPLACE INTO sync_status (key, value) VALUES
('last_push_attempt', datetime('now'));
"
if [ "$FAILED_FINAL" -gt 0 ]; then
echo "$(date): WARNING: $FAILED_FINAL changes failed to push"
echo "$(date): These changes will be retried on next push attempt"
fi
if [ "$REMAINING_COUNT" -gt 0 ]; then
echo "$(date): NOTE: $REMAINING_COUNT changes still staged"
fi
echo "$(date): Push to remote API completed"
```