This is page 44 of 47. Use http://codebase.md/doobidoo/mcp-memory-service?lines=true&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
--------------------------------------------------------------------------------
/install.py:
--------------------------------------------------------------------------------
```python
1 | #!/usr/bin/env python3
2 | # Copyright 2024 Heinrich Krupp
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | """
17 | Installation script for MCP Memory Service with cross-platform compatibility.
18 | This script guides users through the installation process with the appropriate
19 | dependencies for their platform.
20 | """
21 | import os
22 | import sys
23 | import platform
24 | import subprocess
25 | import argparse
26 | import shutil
27 | from pathlib import Path
28 |
29 | # Import shared GPU detection utilities
30 | try:
31 | from src.mcp_memory_service.utils.gpu_detection import detect_gpu as shared_detect_gpu
32 | except ImportError:
33 | # Fallback for development/testing scenarios
34 | sys.path.insert(0, str(Path(__file__).parent))
35 | from src.mcp_memory_service.utils.gpu_detection import detect_gpu as shared_detect_gpu
36 |
37 | # Fix Windows console encoding issues
38 | if platform.system() == "Windows":
39 | # Ensure stdout uses UTF-8 on Windows to prevent character encoding issues in logs
40 | if hasattr(sys.stdout, 'reconfigure'):
41 | try:
42 | sys.stdout.reconfigure(encoding='utf-8')
43 | sys.stderr.reconfigure(encoding='utf-8')
44 | except AttributeError:
45 | pass
46 |
47 | # Enhanced logging system for installer
48 | import logging
49 | from datetime import datetime
50 |
51 | class DualOutput:
52 | """Class to handle both console and file output simultaneously."""
53 | def __init__(self, log_file_path):
54 | self.console = sys.stdout
55 | self.log_file = None
56 | self.log_file_path = log_file_path
57 | self._setup_log_file()
58 |
59 | def _setup_log_file(self):
60 | """Set up the log file with proper encoding."""
61 | try:
62 | # Create log file with UTF-8 encoding
63 | self.log_file = open(self.log_file_path, 'w', encoding='utf-8')
64 | # Write header
65 | timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
66 |
67 | # Fix Windows version display in log header
68 | platform_info = f"{platform.system()} {platform.release()}"
69 | if platform.system() == "Windows":
70 | try:
71 | import winreg
72 | key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion")
73 | build_number = winreg.QueryValueEx(key, "CurrentBuildNumber")[0]
74 | winreg.CloseKey(key)
75 |
76 | # Windows 11 has build number >= 22000
77 | if int(build_number) >= 22000:
78 | platform_info = f"Windows 11"
79 | else:
80 | platform_info = f"Windows {platform.release()}"
81 | except (ImportError, OSError, ValueError):
82 | pass # Use default
83 |
84 | header = f"""
85 | ================================================================================
86 | MCP Memory Service Installation Log
87 | Started: {timestamp}
88 | Platform: {platform_info} ({platform.machine()})
89 | Python: {sys.version}
90 | ================================================================================
91 |
92 | """
93 | self.log_file.write(header)
94 | self.log_file.flush()
95 | except Exception as e:
96 | print(f"Warning: Could not create log file {self.log_file_path}: {e}")
97 | self.log_file = None
98 |
99 | def write(self, text):
100 | """Write to both console and log file."""
101 | # Write to console
102 | self.console.write(text)
103 | self.console.flush()
104 |
105 | # Write to log file if available
106 | if self.log_file:
107 | try:
108 | self.log_file.write(text)
109 | self.log_file.flush()
110 | except Exception:
111 | pass # Silently ignore log file write errors
112 |
113 | def flush(self):
114 | """Flush both outputs."""
115 | self.console.flush()
116 | if self.log_file:
117 | try:
118 | self.log_file.flush()
119 | except Exception:
120 | pass
121 |
122 | def close(self):
123 | """Close the log file."""
124 | if self.log_file:
125 | try:
126 | timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
127 | footer = f"""
128 | ================================================================================
129 | Installation completed: {timestamp}
130 | ================================================================================
131 | """
132 | self.log_file.write(footer)
133 | self.log_file.close()
134 | except Exception:
135 | pass
136 |
137 | # Global dual output instance
138 | _dual_output = None
139 |
140 | def setup_installer_logging():
141 | """Set up the installer logging system."""
142 | global _dual_output
143 |
144 | # Create log file path
145 | log_file = Path.cwd() / "installation.log"
146 |
147 | # Remove old log file if it exists
148 | if log_file.exists():
149 | try:
150 | log_file.unlink()
151 | except Exception:
152 | pass
153 |
154 | # Set up dual output
155 | _dual_output = DualOutput(str(log_file))
156 |
157 | # Redirect stdout to dual output
158 | sys.stdout = _dual_output
159 |
160 | print(f"Installation log will be saved to: {log_file}")
161 |
162 | return str(log_file)
163 |
164 | def cleanup_installer_logging():
165 | """Clean up the installer logging system."""
166 | global _dual_output
167 |
168 | if _dual_output:
169 | # Restore original stdout
170 | sys.stdout = _dual_output.console
171 | _dual_output.close()
172 | _dual_output = None
173 |
174 | # Import Claude commands utilities
175 | try:
176 | from scripts.claude_commands_utils import install_claude_commands, check_claude_code_cli
177 | except ImportError:
178 | # Handle case where script is run from different directory
179 | script_dir = Path(__file__).parent
180 | sys.path.insert(0, str(script_dir))
181 | try:
182 | from scripts.claude_commands_utils import install_claude_commands, check_claude_code_cli
183 | except ImportError:
184 | install_claude_commands = None
185 | check_claude_code_cli = None
186 |
187 | # Global variable to store the uv executable path
188 | UV_EXECUTABLE_PATH = None
189 |
190 | def print_header(text):
191 | """Print a formatted header."""
192 | print("\n" + "=" * 80)
193 | print(f" {text}")
194 | print("=" * 80)
195 |
196 | def print_step(step, text):
197 | """Print a formatted step."""
198 | print(f"\n[{step}] {text}")
199 |
200 | def print_info(text):
201 | """Print formatted info text."""
202 | print(f" -> {text}")
203 |
204 | def print_error(text):
205 | """Print formatted error text."""
206 | print(f" [ERROR] {text}")
207 |
208 | def print_success(text):
209 | """Print formatted success text."""
210 | print(f" [OK] {text}")
211 |
212 | def print_warning(text):
213 | """Print formatted warning text."""
214 | print(f" [WARNING] {text}")
215 |
216 | def prompt_user_input(prompt_text, default_value=""):
217 | """
218 | Prompt user for input with formatted banner.
219 |
220 | Args:
221 | prompt_text: The input prompt to display
222 | default_value: Optional default value if user presses Enter
223 |
224 | Returns:
225 | User's input (or default if empty)
226 | """
227 | print("\n" + "=" * 60)
228 | print("⚠️ USER INPUT REQUIRED")
229 | print("=" * 60)
230 | response = input(prompt_text).strip()
231 | print("=" * 60 + "\n")
232 | return response if response else default_value
233 |
234 | def build_mcp_server_config(storage_backend="sqlite_vec", repo_path=None):
235 | """
236 | Build MCP server configuration dict for multi-client access.
237 |
238 | Args:
239 | storage_backend: Storage backend to use (sqlite_vec or chromadb)
240 | repo_path: Repository path (defaults to current directory)
241 |
242 | Returns:
243 | Dict containing MCP server configuration with command, args, and env
244 | """
245 | if repo_path is None:
246 | repo_path = str(Path.cwd())
247 |
248 | # Build environment configuration based on storage backend
249 | env_config = {
250 | "MCP_MEMORY_STORAGE_BACKEND": storage_backend,
251 | "LOG_LEVEL": "INFO"
252 | }
253 |
254 | # Add backend-specific configuration
255 | if storage_backend == "sqlite_vec":
256 | env_config["MCP_MEMORY_SQLITE_PRAGMAS"] = "busy_timeout=15000,cache_size=20000"
257 |
258 | return {
259 | "command": UV_EXECUTABLE_PATH or "uv",
260 | "args": ["--directory", repo_path, "run", "memory"],
261 | "env": env_config
262 | }
263 |
264 | # Cache for system detection to avoid duplicate calls
265 | _system_info_cache = None
266 |
267 | def detect_system():
268 | """Detect the system architecture and platform."""
269 | global _system_info_cache
270 | if _system_info_cache is not None:
271 | return _system_info_cache
272 |
273 | system = platform.system().lower()
274 | machine = platform.machine().lower()
275 | python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
276 |
277 | is_windows = system == "windows"
278 | is_macos = system == "darwin"
279 | is_linux = system == "linux"
280 | is_arm = machine in ("arm64", "aarch64")
281 | is_x86 = machine in ("x86_64", "amd64", "x64")
282 |
283 | # Fix Windows version detection - Windows 11 reports as Windows 10
284 | if is_windows:
285 | try:
286 | import winreg
287 | key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion")
288 | build_number = winreg.QueryValueEx(key, "CurrentBuildNumber")[0]
289 | winreg.CloseKey(key)
290 |
291 | # Windows 11 has build number >= 22000
292 | if int(build_number) >= 22000:
293 | windows_version = "11"
294 | else:
295 | windows_version = platform.release()
296 | except (ImportError, OSError, ValueError):
297 | windows_version = platform.release()
298 |
299 | print_info(f"System: {platform.system()} {windows_version}")
300 | else:
301 | print_info(f"System: {platform.system()} {platform.release()}")
302 |
303 | print_info(f"Architecture: {machine}")
304 | print_info(f"Python: {python_version}")
305 |
306 | # Check for virtual environment
307 | in_venv = sys.prefix != sys.base_prefix
308 | if not in_venv:
309 | print_warning("Not running in a virtual environment. It's recommended to install in a virtual environment.")
310 | else:
311 | print_info(f"Virtual environment: {sys.prefix}")
312 |
313 | # Check for Homebrew PyTorch installation
314 | has_homebrew_pytorch = False
315 | homebrew_pytorch_version = None
316 | if is_macos:
317 | try:
318 | # Check if pytorch is installed via brew
319 | print_info("Checking for Homebrew PyTorch installation...")
320 | result = subprocess.run(
321 | ['brew', 'list', 'pytorch', '--version'],
322 | capture_output=True,
323 | text=True,
324 | timeout=30 # Increased timeout to prevent hanging
325 | )
326 | if result.returncode == 0:
327 | has_homebrew_pytorch = True
328 | # Extract version from output
329 | version_line = result.stdout.strip()
330 | homebrew_pytorch_version = version_line.split()[1] if len(version_line.split()) > 1 else "Unknown"
331 | print_info(f"Detected Homebrew PyTorch installation: {homebrew_pytorch_version}")
332 | except subprocess.TimeoutExpired:
333 | print_info("Homebrew PyTorch detection timed out - skipping")
334 | has_homebrew_pytorch = False
335 | except (subprocess.SubprocessError, FileNotFoundError):
336 | pass
337 |
338 | _system_info_cache = {
339 | "system": system,
340 | "machine": machine,
341 | "python_version": python_version,
342 | "is_windows": is_windows,
343 | "is_macos": is_macos,
344 | "is_linux": is_linux,
345 | "is_arm": is_arm,
346 | "is_x86": is_x86,
347 | "in_venv": in_venv,
348 | "has_homebrew_pytorch": has_homebrew_pytorch,
349 | "homebrew_pytorch_version": homebrew_pytorch_version
350 | }
351 | return _system_info_cache
352 |
353 | def check_sqlite_extension_support():
354 | """Check if Python's sqlite3 supports loading extensions."""
355 | import sqlite3
356 |
357 | test_conn = None
358 | try:
359 | test_conn = sqlite3.connect(":memory:")
360 | if not hasattr(test_conn, 'enable_load_extension'):
361 | return False, "Python sqlite3 module not compiled with extension support"
362 |
363 | # Test if we can actually enable extension loading
364 | test_conn.enable_load_extension(True)
365 | test_conn.enable_load_extension(False)
366 | return True, "Extension loading supported"
367 |
368 | except AttributeError as e:
369 | return False, f"enable_load_extension not available: {e}"
370 | except Exception as e:
371 | return False, f"Extension support check failed: {e}"
372 | finally:
373 | if test_conn:
374 | test_conn.close()
375 |
376 | def detect_gpu():
377 | """Detect GPU and acceleration capabilities.
378 |
379 | Wrapper function that uses the shared GPU detection module.
380 | """
381 | system_info = detect_system()
382 |
383 | # Use shared GPU detection module
384 | gpu_info = shared_detect_gpu(system_info)
385 |
386 | # Print GPU information (maintain installer output format)
387 | if gpu_info.get("has_cuda"):
388 | cuda_version = gpu_info.get("cuda_version")
389 | print_info(f"CUDA detected: {cuda_version or 'Unknown version'}")
390 | if gpu_info.get("has_rocm"):
391 | rocm_version = gpu_info.get("rocm_version")
392 | print_info(f"ROCm detected: {rocm_version or 'Unknown version'}")
393 | if gpu_info.get("has_mps"):
394 | print_info("Apple Metal Performance Shaders (MPS) detected")
395 | if gpu_info.get("has_directml"):
396 | directml_version = gpu_info.get("directml_version")
397 | if directml_version:
398 | print_info(f"DirectML detected: {directml_version}")
399 | else:
400 | print_info("DirectML detected")
401 |
402 | if not (gpu_info.get("has_cuda") or gpu_info.get("has_rocm") or
403 | gpu_info.get("has_mps") or gpu_info.get("has_directml")):
404 | print_info("No GPU acceleration detected, will use CPU-only mode")
405 |
406 | return gpu_info
407 |
408 |
409 | def check_dependencies():
410 | """Check for required dependencies.
411 |
412 | Note on package managers:
413 | - Traditional virtual environments (venv, virtualenv) include pip by default
414 | - Alternative package managers like uv may not include pip or may manage packages differently
415 | - We attempt multiple detection methods for pip and only fail if:
416 | a) We're not in a virtual environment, or
417 | b) We can't detect pip AND can't install dependencies
418 |
419 | We proceed with installation even if pip isn't detected when in a virtual environment,
420 | assuming an alternative package manager (like uv) is handling dependencies.
421 |
422 | Returns:
423 | bool: True if all dependencies are met, False otherwise.
424 | """
425 | print_step("2", "Checking dependencies")
426 |
427 | # Check for pip
428 | pip_installed = False
429 |
430 | # Try subprocess check first
431 | try:
432 | subprocess.check_call([sys.executable, '-m', 'pip', '--version'],
433 | stdout=subprocess.DEVNULL,
434 | stderr=subprocess.DEVNULL)
435 | pip_installed = True
436 | print_info("pip is installed")
437 | except subprocess.SubprocessError:
438 | # Fallback to import check
439 | try:
440 | import pip
441 | pip_installed = True
442 | print_info(f"pip is installed: {pip.__version__}")
443 | except ImportError:
444 | # Check if we're in a virtual environment
445 | in_venv = sys.prefix != sys.base_prefix
446 | if in_venv:
447 | print_warning("pip could not be detected, but you're in a virtual environment. "
448 | "If you're using uv or another alternative package manager, this is normal. "
449 | "Continuing installation...")
450 | pip_installed = True # Proceed anyway
451 | else:
452 | print_error("pip is not installed. Please install pip first.")
453 | return False
454 |
455 | # Check for setuptools
456 | try:
457 | import setuptools
458 | print_info(f"setuptools is installed: {setuptools.__version__}")
459 | except ImportError:
460 | print_warning("setuptools is not installed. Will attempt to install it.")
461 | # If pip is available, use it to install setuptools
462 | if pip_installed:
463 | try:
464 | subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'setuptools'],
465 | stdout=subprocess.DEVNULL)
466 | print_success("setuptools installed successfully")
467 | except subprocess.SubprocessError:
468 | # Check if in virtual environment
469 | in_venv = sys.prefix != sys.base_prefix
470 | if in_venv:
471 | print_warning("Failed to install setuptools with pip. If you're using an alternative package manager "
472 | "like uv, please install setuptools manually using that tool (e.g., 'uv pip install setuptools').")
473 | else:
474 | print_error("Failed to install setuptools. Please install it manually.")
475 | return False
476 | else:
477 | # Should be unreachable since pip_installed would only be False if we returned earlier
478 | print_error("Cannot install setuptools without pip. Please install setuptools manually.")
479 | return False
480 |
481 | # Check for wheel
482 | try:
483 | import wheel
484 | print_info(f"wheel is installed: {wheel.__version__}")
485 | except ImportError:
486 | print_warning("wheel is not installed. Will attempt to install it.")
487 | # If pip is available, use it to install wheel
488 | if pip_installed:
489 | try:
490 | subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'wheel'],
491 | stdout=subprocess.DEVNULL)
492 | print_success("wheel installed successfully")
493 | except subprocess.SubprocessError:
494 | # Check if in virtual environment
495 | in_venv = sys.prefix != sys.base_prefix
496 | if in_venv:
497 | print_warning("Failed to install wheel with pip. If you're using an alternative package manager "
498 | "like uv, please install wheel manually using that tool (e.g., 'uv pip install wheel').")
499 | else:
500 | print_error("Failed to install wheel. Please install it manually.")
501 | return False
502 | else:
503 | # Should be unreachable since pip_installed would only be False if we returned earlier
504 | print_error("Cannot install wheel without pip. Please install wheel manually.")
505 | return False
506 |
507 | return True
508 |
509 | def install_pytorch_platform_specific(system_info, gpu_info, args=None):
510 | """Install PyTorch with platform-specific configurations."""
511 | # Check if PyTorch installation should be skipped
512 | if args and args.skip_pytorch:
513 | print_info("Skipping PyTorch installation as requested")
514 | return True
515 |
516 | if system_info["is_windows"]:
517 | return install_pytorch_windows(gpu_info)
518 | elif system_info["is_macos"] and system_info["is_x86"]:
519 | return install_pytorch_macos_intel()
520 | elif system_info["is_macos"] and system_info["is_arm"]:
521 | return install_pytorch_macos_arm64()
522 | else:
523 | # For other platforms, let the regular installer handle it
524 | return True
525 |
526 | def install_pytorch_macos_intel():
527 | """Install PyTorch specifically for macOS with Intel CPUs."""
528 | print_step("3a", "Installing PyTorch for macOS Intel CPU")
529 |
530 | # Use the versions known to work well on macOS Intel and with Python 3.13+
531 | try:
532 | # For Python 3.13+, we need newer PyTorch versions
533 | python_version = sys.version_info
534 |
535 | if python_version >= (3, 13):
536 | # For Python 3.13+, try to install latest compatible version
537 | print_info(f"Installing PyTorch for macOS Intel (Python {python_version.major}.{python_version.minor})...")
538 | print_info("Attempting to install latest PyTorch compatible with Python 3.13...")
539 |
540 | try:
541 | # Try to install without version specifiers to get latest compatible version
542 | cmd = [
543 | sys.executable, '-m', 'pip', 'install',
544 | "torch", "torchvision", "torchaudio"
545 | ]
546 | print_info(f"Running: {' '.join(cmd)}")
547 | subprocess.check_call(cmd)
548 | st_version = "3.0.0" # Newer sentence-transformers for newer PyTorch
549 | except subprocess.SubprocessError as e:
550 | print_warning(f"Failed to install latest PyTorch: {e}")
551 | # Fallback to a specific version
552 | torch_version = "2.1.0"
553 | torch_vision_version = "0.16.0"
554 | torch_audio_version = "2.1.0"
555 | st_version = "3.0.0"
556 |
557 | print_info(f"Trying fallback to PyTorch {torch_version}...")
558 |
559 | cmd = [
560 | sys.executable, '-m', 'pip', 'install',
561 | f"torch=={torch_version}",
562 | f"torchvision=={torch_vision_version}",
563 | f"torchaudio=={torch_audio_version}"
564 | ]
565 | print_info(f"Running: {' '.join(cmd)}")
566 | subprocess.check_call(cmd)
567 | else:
568 | # Use traditional versions for older Python
569 | torch_version = "1.13.1"
570 | torch_vision_version = "0.14.1"
571 | torch_audio_version = "0.13.1"
572 | st_version = "2.2.2"
573 |
574 | print_info(f"Installing PyTorch {torch_version} for macOS Intel (Python {python_version.major}.{python_version.minor})...")
575 |
576 | # Install PyTorch first with compatible version
577 | cmd = [
578 | sys.executable, '-m', 'pip', 'install',
579 | f"torch=={torch_version}",
580 | f"torchvision=={torch_vision_version}",
581 | f"torchaudio=={torch_audio_version}"
582 | ]
583 |
584 | print_info(f"Running: {' '.join(cmd)}")
585 | subprocess.check_call(cmd)
586 |
587 | # Install a compatible version of sentence-transformers
588 | print_info(f"Installing sentence-transformers {st_version}...")
589 |
590 | cmd = [
591 | sys.executable, '-m', 'pip', 'install',
592 | f"sentence-transformers=={st_version}"
593 | ]
594 |
595 | print_info(f"Running: {' '.join(cmd)}")
596 | subprocess.check_call(cmd)
597 |
598 | print_success(f"PyTorch {torch_version} and sentence-transformers {st_version} installed successfully for macOS Intel")
599 | return True
600 | except subprocess.SubprocessError as e:
601 | print_error(f"Failed to install PyTorch for macOS Intel: {e}")
602 |
603 | # Provide fallback instructions
604 | if python_version >= (3, 13):
605 | print_warning("You may need to manually install compatible versions for Python 3.13+ on Intel macOS:")
606 | print_info("pip install torch==2.3.0 torchvision==0.18.0 torchaudio==2.3.0")
607 | print_info("pip install sentence-transformers==3.0.0")
608 | else:
609 | print_warning("You may need to manually install compatible versions for Intel macOS:")
610 | print_info("pip install torch==1.13.1 torchvision==0.14.1 torchaudio==0.13.1")
611 | print_info("pip install sentence-transformers==2.2.2")
612 |
613 | return False
614 |
615 | def install_pytorch_macos_arm64():
616 | """Install PyTorch specifically for macOS with ARM64 (Apple Silicon)."""
617 | print_step("3a", "Installing PyTorch for macOS ARM64 (Apple Silicon)")
618 |
619 | try:
620 | # For Apple Silicon, we can use the latest PyTorch with MPS support
621 | print_info("Installing PyTorch with Metal Performance Shaders (MPS) support...")
622 |
623 | # Install PyTorch with MPS support - let pip choose the best compatible version
624 | cmd = [
625 | sys.executable, '-m', 'pip', 'install',
626 | 'torch>=2.0.0',
627 | 'torchvision',
628 | 'torchaudio'
629 | ]
630 |
631 | print_info(f"Running: {' '.join(cmd)}")
632 | subprocess.check_call(cmd)
633 |
634 | # Install sentence-transformers
635 | print_info("Installing sentence-transformers (for embedding generation)...")
636 | print_info("Note: Models will be downloaded on first use (~25MB)")
637 | cmd = [
638 | sys.executable, '-m', 'pip', 'install',
639 | 'sentence-transformers>=2.2.2'
640 | ]
641 |
642 | print_info(f"Running: {' '.join(cmd)}")
643 | subprocess.check_call(cmd)
644 |
645 | print_success("PyTorch and sentence-transformers installed successfully for macOS ARM64")
646 | print_info("MPS (Metal Performance Shaders) acceleration is available for GPU compute")
647 |
648 | return True
649 | except subprocess.SubprocessError as e:
650 | print_error(f"Failed to install PyTorch for macOS ARM64: {e}")
651 |
652 | # Provide fallback instructions
653 | print_warning("You may need to manually install PyTorch for Apple Silicon:")
654 | print_info("pip install torch torchvision torchaudio")
655 | print_info("pip install sentence-transformers")
656 | print_info("")
657 | print_info("If you encounter issues, try:")
658 | print_info("pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/cpu")
659 |
660 | return False
661 |
662 | def install_pytorch_windows(gpu_info):
663 | """Install PyTorch on Windows using the appropriate index URL."""
664 | print_step("3a", "Installing PyTorch for Windows")
665 |
666 | # Check if PyTorch is already installed and compatible
667 | pytorch_installed = False
668 | torch_version_installed = None
669 | directml_compatible = False
670 |
671 | try:
672 | import torch
673 | torch_version_installed = torch.__version__
674 | pytorch_installed = True
675 | print_info(f"PyTorch {torch_version_installed} is already installed")
676 |
677 | # Check if version is compatible with DirectML (2.4.x works, 2.5.x doesn't)
678 | version_parts = torch_version_installed.split('.')
679 | major, minor = int(version_parts[0]), int(version_parts[1])
680 |
681 | if gpu_info["has_directml"]:
682 | if major == 2 and minor == 4:
683 | directml_compatible = True
684 | print_success(f"PyTorch {torch_version_installed} is compatible with DirectML")
685 |
686 | # Check if torch-directml is also installed
687 | try:
688 | import torch_directml
689 | directml_version = getattr(torch_directml, '__version__', 'Unknown version')
690 | print_success(f"torch-directml {directml_version} is already installed")
691 | return True # Everything is compatible, no need to reinstall
692 | except ImportError:
693 | print_info("torch-directml not found, will install it")
694 | # Install torch-directml only
695 | try:
696 | subprocess.check_call([
697 | sys.executable, '-m', 'pip', 'install', 'torch-directml==0.2.5.dev240914'
698 | ])
699 | print_success("torch-directml installed successfully")
700 | return True
701 | except subprocess.SubprocessError:
702 | print_warning("Failed to install torch-directml - DirectML support will be limited")
703 | return True # Still return True since PyTorch works
704 |
705 | elif major == 2 and minor >= 5:
706 | print_warning(f"PyTorch {torch_version_installed} is not compatible with torch-directml")
707 | print_info("torch-directml requires PyTorch 2.4.x, but 2.5.x is installed")
708 | print_info("Keeping existing PyTorch installation - DirectML support will be limited")
709 | return True # Don't break existing installation
710 | else:
711 | print_info(f"PyTorch {torch_version_installed} compatibility with DirectML is unknown")
712 | else:
713 | # No DirectML needed, check if current version is reasonable
714 | if major == 2 and minor >= 4:
715 | print_success(f"PyTorch {torch_version_installed} is acceptable for CPU usage")
716 | return True # Keep existing installation
717 |
718 | except ImportError:
719 | print_info("PyTorch not found, will install compatible version")
720 |
721 | # If we get here, we need to install PyTorch
722 | # Determine the appropriate PyTorch index URL based on GPU
723 | if gpu_info["has_cuda"]:
724 | # Get CUDA version and determine appropriate index URL
725 | cuda_version = gpu_info.get("cuda_version", "")
726 |
727 | # Extract major version from CUDA version string
728 | cuda_major = None
729 | if cuda_version:
730 | # Try to extract the major version (e.g., "11.8" -> "11")
731 | try:
732 | cuda_major = cuda_version.split('.')[0]
733 | except (IndexError, AttributeError):
734 | pass
735 |
736 | # Default to cu118 if we couldn't determine the version or it's not a common one
737 | if cuda_major == "12":
738 | cuda_suffix = "cu121" # CUDA 12.x
739 | print_info(f"Detected CUDA {cuda_version}, using cu121 channel")
740 | elif cuda_major == "11":
741 | cuda_suffix = "cu118" # CUDA 11.x
742 | print_info(f"Detected CUDA {cuda_version}, using cu118 channel")
743 | elif cuda_major == "10":
744 | cuda_suffix = "cu102" # CUDA 10.x
745 | print_info(f"Detected CUDA {cuda_version}, using cu102 channel")
746 | else:
747 | # Default to cu118 as a safe choice for newer NVIDIA GPUs
748 | cuda_suffix = "cu118"
749 | print_info(f"Using default cu118 channel for CUDA {cuda_version}")
750 |
751 | index_url = f"https://download.pytorch.org/whl/{cuda_suffix}"
752 | else:
753 | # CPU-only version
754 | index_url = "https://download.pytorch.org/whl/cpu"
755 | print_info("Using CPU-only PyTorch for Windows")
756 |
757 | # Install PyTorch with the appropriate index URL
758 | try:
759 | # Use versions compatible with DirectML if needed
760 | if gpu_info["has_directml"]:
761 | # Use PyTorch 2.4.x which is compatible with torch-directml
762 | torch_version = "2.4.1"
763 | torchvision_version = "0.19.1" # Compatible with torch 2.4.1
764 | torchaudio_version = "2.4.1"
765 | print_info("Using PyTorch 2.4.1 for DirectML compatibility")
766 | else:
767 | # Use latest version for non-DirectML systems
768 | torch_version = "2.5.1"
769 | torchvision_version = "0.20.1" # Compatible with torch 2.5.1
770 | torchaudio_version = "2.5.1"
771 | print_info("Using PyTorch 2.5.1 for optimal performance")
772 |
773 | cmd = [
774 | sys.executable, '-m', 'pip', 'install',
775 | f"torch=={torch_version}",
776 | f"torchvision=={torchvision_version}",
777 | f"torchaudio=={torchaudio_version}",
778 | f"--index-url={index_url}"
779 | ]
780 |
781 | print_info(f"Running: {' '.join(cmd)}")
782 | subprocess.check_call(cmd)
783 |
784 | # Check if DirectML is needed
785 | if gpu_info["has_directml"]:
786 | print_info("Installing torch-directml for DirectML support")
787 | try:
788 | # Try the latest dev version since stable versions aren't available
789 | subprocess.check_call([
790 | sys.executable, '-m', 'pip', 'install', 'torch-directml==0.2.5.dev240914'
791 | ])
792 | except subprocess.SubprocessError:
793 | print_warning("Failed to install torch-directml - DirectML support will be limited")
794 | print_info("You can install manually later with: pip install torch-directml==0.2.5.dev240914")
795 |
796 | print_success("PyTorch installed successfully for Windows")
797 | return True
798 | except subprocess.SubprocessError as e:
799 | print_error(f"Failed to install PyTorch for Windows: {e}")
800 | print_warning("You may need to manually install PyTorch using instructions from https://pytorch.org/get-started/locally/")
801 | return False
802 |
803 | def detect_storage_backend_compatibility(system_info, gpu_info):
804 | """Detect which storage backends are compatible with the current environment."""
805 | print_step("3a", "Analyzing storage backend compatibility")
806 |
807 | compatibility = {
808 | "chromadb": {"supported": True, "issues": [], "recommendation": "legacy"},
809 | "sqlite_vec": {"supported": True, "issues": [], "recommendation": "default"}
810 | }
811 |
812 | # Check ChromaDB compatibility issues
813 | chromadb_issues = []
814 |
815 | # macOS Intel compatibility issues
816 | if system_info["is_macos"] and system_info["is_x86"]:
817 | chromadb_issues.append("ChromaDB has known installation issues on older macOS Intel systems")
818 | chromadb_issues.append("May require specific dependency versions")
819 | compatibility["chromadb"]["recommendation"] = "problematic"
820 | compatibility["sqlite_vec"]["recommendation"] = "recommended"
821 |
822 | # Memory constraints
823 | total_memory_gb = 0
824 | try:
825 | import psutil
826 | total_memory_gb = psutil.virtual_memory().total / (1024**3)
827 | except ImportError:
828 | # Fallback memory detection
829 | try:
830 | with open('/proc/meminfo', 'r') as f:
831 | for line in f:
832 | if line.startswith('MemTotal:'):
833 | total_memory_gb = int(line.split()[1]) / (1024**2)
834 | break
835 | except (FileNotFoundError, IOError):
836 | pass
837 |
838 | if total_memory_gb > 0 and total_memory_gb < 4:
839 | chromadb_issues.append(f"System has {total_memory_gb:.1f}GB RAM - ChromaDB may consume significant memory")
840 | compatibility["sqlite_vec"]["recommendation"] = "recommended"
841 |
842 | # Older Python versions
843 | python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
844 | if sys.version_info < (3, 9):
845 | chromadb_issues.append(f"Python {python_version} may have ChromaDB compatibility issues")
846 |
847 | # ARM architecture considerations
848 | if system_info["is_arm"]:
849 | print_info("ARM architecture detected - both backends should work well")
850 |
851 | compatibility["chromadb"]["issues"] = chromadb_issues
852 |
853 | # Print compatibility analysis
854 | print_info("Storage Backend Compatibility Analysis:")
855 |
856 | for backend, info in compatibility.items():
857 | status = "[OK]" if info["supported"] else "[X]"
858 | rec_text = {
859 | "recommended": "[*] RECOMMENDED",
860 | "default": "[+] Standard",
861 | "problematic": "[!] May have issues",
862 | "lightweight": "[-] Lightweight"
863 | }.get(info["recommendation"], "")
864 |
865 | print_info(f" {status} {backend.upper()}: {rec_text}")
866 |
867 | if info["issues"]:
868 | for issue in info["issues"]:
869 | print_info(f" • {issue}")
870 |
871 | return compatibility
872 |
873 | def choose_storage_backend(system_info, gpu_info, args):
874 | """Choose storage backend based on environment and user preferences."""
875 | compatibility = detect_storage_backend_compatibility(system_info, gpu_info)
876 |
877 | # Check if user specified a backend via environment
878 | env_backend = os.environ.get('MCP_MEMORY_STORAGE_BACKEND')
879 | if env_backend:
880 | print_info(f"Using storage backend from environment: {env_backend}")
881 | return env_backend
882 |
883 | # Check for command line argument (we'll add this)
884 | if hasattr(args, 'storage_backend') and args.storage_backend:
885 | print_info(f"Using storage backend from command line: {args.storage_backend}")
886 | return args.storage_backend
887 |
888 | # Auto-select based on compatibility
889 | recommended_backend = None
890 | for backend, info in compatibility.items():
891 | if info["recommendation"] == "recommended":
892 | recommended_backend = backend
893 | break
894 |
895 | if not recommended_backend:
896 | recommended_backend = "sqlite_vec" # Default fallback
897 |
898 | # Interactive selection if no auto-recommendation is clear
899 | if compatibility["chromadb"]["recommendation"] == "problematic":
900 | print_step("3b", "Storage Backend Selection")
901 | print_info("Based on your system, ChromaDB may have installation issues.")
902 | print_info("SQLite-vec is recommended as a lightweight, compatible alternative.")
903 | print_info("")
904 | print_info("Available options:")
905 | print_info(" 1. SQLite-vec (Recommended) - Lightweight, fast, minimal dependencies")
906 | print_info(" 2. ChromaDB (Standard) - Full-featured but may have issues on your system")
907 | print_info(" 3. Auto-detect - Try ChromaDB first, fallback to SQLite-vec if it fails")
908 | print_info("")
909 |
910 | while True:
911 | try:
912 | if args.non_interactive:
913 | print_info("Non-interactive mode: using default storage backend (SQLite-vec)")
914 | choice = "1"
915 | else:
916 | choice = prompt_user_input("Choose storage backend [1-3] (default: 1, press Enter for default): ", "1")
917 |
918 | if choice == "1":
919 | return "sqlite_vec"
920 | elif choice == "2":
921 | return "chromadb"
922 | elif choice == "3":
923 | return "auto_detect"
924 | else:
925 | print_error("Please enter 1, 2, or 3")
926 | except (EOFError, KeyboardInterrupt):
927 | print_info("\nUsing recommended backend: sqlite_vec")
928 | return "sqlite_vec"
929 |
930 | return recommended_backend
931 |
932 | def install_storage_backend(backend, system_info):
933 | """Install the chosen storage backend."""
934 | print_step("3c", f"Installing {backend} storage backend")
935 |
936 | if backend == "sqlite_vec":
937 | # Check extension support before attempting installation
938 | extension_supported, extension_message = check_sqlite_extension_support()
939 | if not extension_supported:
940 | print_warning(f"SQLite extension support not available: {extension_message}")
941 |
942 | # Provide platform-specific guidance
943 | if platform.system().lower() == "darwin": # macOS
944 | print_info("This is common on macOS with system Python.")
945 | print_info("SOLUTIONS:")
946 | print_info(" • Install Python via Homebrew: brew install python")
947 | print_info(" • Use pyenv with extensions: PYTHON_CONFIGURE_OPTS='--enable-loadable-sqlite-extensions' pyenv install 3.12.0")
948 | print_info(" • Switch to ChromaDB backend: --storage-backend chromadb")
949 |
950 | # Ask user what they want to do
951 | if not system_info.get('non_interactive'):
952 | print("\n" + "=" * 60)
953 | print("⚠️ USER INPUT REQUIRED")
954 | print("=" * 60)
955 | print("sqlite-vec requires SQLite extension support, which is not available.")
956 | response = input("Switch to ChromaDB backend instead? (y/N): ").strip().lower()
957 | print("=" * 60 + "\n")
958 | if response in ['y', 'yes']:
959 | print_info("Switching to ChromaDB backend...")
960 | return install_storage_backend("chromadb", system_info)
961 | else:
962 | print_info("Continuing with sqlite-vec installation (may fail at runtime)...")
963 | else:
964 | print_info("Non-interactive mode: attempting sqlite-vec installation anyway")
965 | else:
966 | print_info("Consider switching to ChromaDB backend for better compatibility")
967 |
968 | # Special handling for Python 3.13
969 | if sys.version_info >= (3, 13):
970 | print_info("Detected Python 3.13+ - using special installation method for sqlite-vec")
971 | return install_sqlite_vec_python313(system_info)
972 |
973 | # Standard installation for older Python versions
974 | try:
975 | print_info("Installing SQLite-vec...")
976 | subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'sqlite-vec'])
977 | print_success("SQLite-vec installed successfully")
978 | return True
979 | except subprocess.SubprocessError as e:
980 | print_error(f"Failed to install SQLite-vec: {e}")
981 | return False
982 |
983 | elif backend == "chromadb":
984 | print_error("ChromaDB backend has been removed in v8.0.0")
985 | print_info("Please use one of the supported backends:")
986 | print_info(" - 'hybrid': Local speed + cloud persistence (recommended)")
987 | print_info(" - 'sqlite_vec': Fast local storage")
988 | print_info(" - 'cloudflare': Cloud storage only")
989 | print_info("\nTo migrate from ChromaDB, run: python scripts/migration/migrate_to_sqlite_vec.py")
990 | return False
991 |
992 | elif backend == "auto_detect":
993 | print_info("Attempting auto-detection...")
994 |
995 | # Try ChromaDB first
996 | print_info("Trying ChromaDB installation...")
997 | if install_storage_backend("chromadb", system_info):
998 | print_success("ChromaDB installed successfully")
999 | return "chromadb"
1000 |
1001 | print_warning("ChromaDB installation failed, falling back to SQLite-vec...")
1002 | if install_storage_backend("sqlite_vec", system_info):
1003 | print_success("SQLite-vec installed successfully as fallback")
1004 | return "sqlite_vec"
1005 |
1006 | print_error("Both storage backends failed to install")
1007 | return False
1008 |
1009 | return False
1010 |
1011 | def initialize_sqlite_vec_database(storage_path):
1012 | """Initialize SQLite-vec database during installation."""
1013 | try:
1014 | print_info("Initializing SQLite-vec database...")
1015 |
1016 | # Add src to path for imports
1017 | src_path = str(Path(__file__).parent / "src")
1018 | if src_path not in sys.path:
1019 | sys.path.insert(0, src_path)
1020 |
1021 | # Import required modules
1022 | from mcp_memory_service.storage.sqlite_vec import SqliteVecMemoryStorage
1023 | from mcp_memory_service.models.memory import Memory
1024 | from mcp_memory_service.utils.hashing import generate_content_hash
1025 | import asyncio
1026 |
1027 | async def init_db():
1028 | # Create storage instance
1029 | storage = SqliteVecMemoryStorage(str(storage_path))
1030 |
1031 | # Initialize the database
1032 | await storage.initialize()
1033 |
1034 | # Create a test memory to verify the database works
1035 | test_content = "Database initialization successful"
1036 | test_memory = Memory(
1037 | content=test_content,
1038 | content_hash=generate_content_hash(test_content),
1039 | tags=["init", "system"],
1040 | memory_type="system"
1041 | )
1042 |
1043 | # Store test memory
1044 | success, message = await storage.store(test_memory)
1045 | return success, message
1046 |
1047 | # Run initialization
1048 | success, message = asyncio.run(init_db())
1049 |
1050 | if success:
1051 | print_success(f"SQLite-vec database initialized: {storage_path}")
1052 | return True
1053 | else:
1054 | print_warning(f"Database initialization partially failed: {message}")
1055 | return True # Database exists even if test failed
1056 |
1057 | except ImportError as e:
1058 | print_warning(f"Could not initialize database (dependencies missing): {e}")
1059 | print_info("Database will be initialized on first use")
1060 | return True # Not a critical failure
1061 | except Exception as e:
1062 | print_warning(f"Database initialization failed: {e}")
1063 | print_info("Database will be initialized on first use")
1064 | return True # Not a critical failure
1065 |
1066 | def install_sqlite_vec_python313(system_info):
1067 | """Special installation method for sqlite-vec on Python 3.13+."""
1068 | print_info("Python 3.13+ detected - sqlite-vec may not have pre-built wheels yet")
1069 |
1070 | # Check if uv is available
1071 | uv_path = shutil.which("uv")
1072 | use_uv = uv_path is not None
1073 |
1074 | # Try multiple installation strategies
1075 | strategies = []
1076 |
1077 | if use_uv:
1078 | # Strategy 1: Try with uv pip
1079 | strategies.append({
1080 | 'name': 'uv pip install',
1081 | 'cmd': [uv_path, 'pip', 'install', 'sqlite-vec'],
1082 | 'description': 'Installing with uv package manager'
1083 | })
1084 |
1085 | # Strategy 2: Try with uv pip and no-binary flag
1086 | strategies.append({
1087 | 'name': 'uv pip install (source build)',
1088 | 'cmd': [uv_path, 'pip', 'install', '--no-binary', ':all:', 'sqlite-vec'],
1089 | 'description': 'Building from source with uv'
1090 | })
1091 |
1092 | # Strategy 3: Standard pip install
1093 | strategies.append({
1094 | 'name': 'pip install',
1095 | 'cmd': [sys.executable, '-m', 'pip', 'install', 'sqlite-vec'],
1096 | 'description': 'Installing with pip'
1097 | })
1098 |
1099 | # Strategy 4: pip with no-binary flag to force compilation
1100 | strategies.append({
1101 | 'name': 'pip install (source build)',
1102 | 'cmd': [sys.executable, '-m', 'pip', 'install', '--no-binary', ':all:', 'sqlite-vec'],
1103 | 'description': 'Building from source with pip'
1104 | })
1105 |
1106 | # Strategy 5: Install from GitHub directly
1107 | strategies.append({
1108 | 'name': 'GitHub install',
1109 | 'cmd': [sys.executable, '-m', 'pip', 'install', 'git+https://github.com/asg017/sqlite-vec.git#subdirectory=python'],
1110 | 'description': 'Installing directly from GitHub'
1111 | })
1112 |
1113 | # Try each strategy
1114 | for i, strategy in enumerate(strategies, 1):
1115 | try:
1116 | print_info(f"Attempt {i}/{len(strategies)}: {strategy['description']}...")
1117 | subprocess.check_call(strategy['cmd'], stderr=subprocess.PIPE)
1118 | print_success(f"SQLite-vec installed successfully using {strategy['name']}")
1119 | return True
1120 | except subprocess.CalledProcessError as e:
1121 | print_warning(f"{strategy['name']} failed: {e}")
1122 | if i < len(strategies):
1123 | print_info("Trying next installation method...")
1124 | continue
1125 | except Exception as e:
1126 | print_warning(f"{strategy['name']} failed with unexpected error: {e}")
1127 | continue
1128 |
1129 | # All strategies failed - provide manual instructions
1130 | print_error("Failed to install sqlite-vec with all automatic methods")
1131 | print_info("")
1132 | print_info("MANUAL INSTALLATION OPTIONS:")
1133 | print_info("")
1134 | print_info("Option 1: Use Python 3.12 (recommended)")
1135 | print_info(" 1. Install Python 3.12: brew install [email protected]")
1136 | print_info(" 2. Create venv: python3.12 -m venv .venv")
1137 | print_info(" 3. Activate: source .venv/bin/activate")
1138 | print_info(" 4. Re-run: python install.py")
1139 | print_info("")
1140 | print_info("Option 2: Install pysqlite3-binary (alternative)")
1141 | print_info(" pip install pysqlite3-binary")
1142 | print_info("")
1143 | print_info("Option 3: Wait for sqlite-vec Python 3.13 support")
1144 | print_info(" Check: https://github.com/asg017/sqlite-vec/issues")
1145 | print_info("")
1146 | print_info("Option 4: Use hybrid or cloudflare backend")
1147 | print_info(" python install.py --storage-backend hybrid")
1148 | print_info("")
1149 |
1150 | # Note: ChromaDB option removed in v8.0.0
1151 |
1152 | return False
1153 |
1154 | def install_uv():
1155 | """Install uv package manager if not already installed."""
1156 | uv_path = shutil.which("uv")
1157 | if uv_path:
1158 | print_info(f"uv is already installed at: {uv_path}")
1159 | return uv_path
1160 |
1161 | print_info("Installing uv package manager...")
1162 |
1163 | try:
1164 | # Determine the installation directory
1165 | if platform.system() == 'Windows':
1166 | # On Windows, install to user's AppData/Local
1167 | install_dir = os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'uv')
1168 | else:
1169 | # On Unix-like systems, install to ~/.local/bin
1170 | install_dir = os.path.expanduser("~/.local/bin")
1171 |
1172 | # Create installation directory if it doesn't exist
1173 | os.makedirs(install_dir, exist_ok=True)
1174 |
1175 | # Download and install uv
1176 | if platform.system() == 'Windows':
1177 | # Windows installation
1178 | install_script = "powershell -c \"irm https://astral.sh/uv/install.ps1 | iex\""
1179 | subprocess.check_call(install_script, shell=True)
1180 | else:
1181 | # Unix-like installation
1182 | install_script = "curl -LsSf https://astral.sh/uv/install.sh | sh"
1183 | subprocess.check_call(install_script, shell=True)
1184 |
1185 | # Check if uv was installed successfully
1186 | uv_path = shutil.which("uv")
1187 | if not uv_path:
1188 | # Try common installation paths
1189 | if platform.system() == 'Windows':
1190 | possible_paths = [
1191 | os.path.join(install_dir, 'uv.exe'),
1192 | os.path.join(os.environ.get('USERPROFILE', ''), '.cargo', 'bin', 'uv.exe')
1193 | ]
1194 | else:
1195 | possible_paths = [
1196 | os.path.join(install_dir, 'uv'),
1197 | os.path.expanduser("~/.cargo/bin/uv")
1198 | ]
1199 |
1200 | for path in possible_paths:
1201 | if os.path.exists(path):
1202 | uv_path = path
1203 | break
1204 |
1205 | if uv_path:
1206 | print_success(f"uv installed successfully at: {uv_path}")
1207 | return uv_path
1208 | else:
1209 | print_error("uv installation completed but executable not found in PATH")
1210 | print_info("You may need to add the installation directory to your PATH")
1211 | return None
1212 |
1213 | except subprocess.CalledProcessError as e:
1214 | print_error(f"Failed to install uv: {e}")
1215 | return None
1216 | except Exception as e:
1217 | print_error(f"Unexpected error installing uv: {e}")
1218 | return None
1219 |
1220 | def _setup_installer_command():
1221 | """Set up the installer command prefix (pip or uv).
1222 |
1223 | Returns:
1224 | tuple: (installer_cmd: list, uv_path: str or None)
1225 | """
1226 | # Detect if pip is available
1227 | pip_available = False
1228 | try:
1229 | subprocess.check_call([sys.executable, '-m', 'pip', '--version'],
1230 | stdout=subprocess.DEVNULL,
1231 | stderr=subprocess.DEVNULL)
1232 | pip_available = True
1233 | except subprocess.SubprocessError:
1234 | pip_available = False
1235 |
1236 | # Try to install uv if pip is not available
1237 | if not pip_available:
1238 | print_info("pip not found, attempting to install uv package manager...")
1239 | uv_path = install_uv()
1240 | else:
1241 | # Check if uv is already available
1242 | uv_path = shutil.which("uv")
1243 | if uv_path:
1244 | print_info(f"uv package manager found at: {uv_path}")
1245 | else:
1246 | print_info("uv package manager not found (will use pip for installation)")
1247 |
1248 | # Store the uv path globally for config generation
1249 | global UV_EXECUTABLE_PATH
1250 | UV_EXECUTABLE_PATH = uv_path
1251 |
1252 | # Decide installer command prefix
1253 | if pip_available:
1254 | installer_cmd = [sys.executable, '-m', 'pip']
1255 | elif uv_path:
1256 | installer_cmd = [uv_path, 'pip']
1257 | print_info(f"Using uv for installation: {uv_path}")
1258 | else:
1259 | print_error("Neither pip nor uv could be found or installed. Cannot install packages.")
1260 | return None, None
1261 |
1262 | return installer_cmd, uv_path
1263 |
1264 |
1265 | def _configure_storage_and_gpu(args):
1266 | """Configure storage backend and GPU environment variables.
1267 |
1268 | Args:
1269 | args: Parsed command line arguments
1270 |
1271 | Returns:
1272 | tuple: (env: dict, system_info: dict, gpu_info: dict, chosen_backend: str)
1273 | """
1274 | env = os.environ.copy()
1275 |
1276 | # Get system and GPU info
1277 | system_info = detect_system()
1278 | gpu_info = detect_gpu()
1279 |
1280 | # Choose and install storage backend
1281 | chosen_backend = choose_storage_backend(system_info, gpu_info, args)
1282 | if chosen_backend == "auto_detect":
1283 | # Handle auto-detection case
1284 | actual_backend = install_storage_backend(chosen_backend, system_info)
1285 | if not actual_backend:
1286 | print_error("Failed to install any storage backend")
1287 | return None, None, None, None
1288 | chosen_backend = actual_backend
1289 | else:
1290 | # Install the chosen backend
1291 | if not install_storage_backend(chosen_backend, system_info):
1292 | print_error(f"Failed to install {chosen_backend} storage backend")
1293 | return None, None, None, None
1294 |
1295 | # Set environment variable for chosen backend
1296 | if chosen_backend == "sqlite_vec":
1297 | env['MCP_MEMORY_STORAGE_BACKEND'] = 'sqlite_vec'
1298 | os.environ['MCP_MEMORY_STORAGE_BACKEND'] = 'sqlite_vec'
1299 | print_info("Configured to use SQLite-vec storage backend")
1300 | else:
1301 | env['MCP_MEMORY_STORAGE_BACKEND'] = 'chromadb'
1302 | os.environ['MCP_MEMORY_STORAGE_BACKEND'] = 'chromadb'
1303 | print_info("Configured to use ChromaDB storage backend")
1304 |
1305 | # Set environment variables based on detected GPU
1306 | if gpu_info.get("has_cuda"):
1307 | print_info("Configuring for CUDA installation")
1308 | elif gpu_info.get("has_rocm"):
1309 | print_info("Configuring for ROCm installation")
1310 | env['MCP_MEMORY_USE_ROCM'] = '1'
1311 | elif gpu_info.get("has_mps"):
1312 | print_info("Configuring for Apple Silicon MPS installation")
1313 | env['PYTORCH_ENABLE_MPS_FALLBACK'] = '1'
1314 | elif gpu_info.get("has_directml"):
1315 | print_info("Configuring for DirectML installation")
1316 | env['MCP_MEMORY_USE_DIRECTML'] = '1'
1317 | else:
1318 | print_info("Configuring for CPU-only installation")
1319 | env['MCP_MEMORY_USE_ONNX'] = '1'
1320 |
1321 | return env, system_info, gpu_info, chosen_backend
1322 |
1323 |
1324 | def _handle_pytorch_setup(args, system_info, gpu_info, env):
1325 | """Handle PyTorch installation logic.
1326 |
1327 | Args:
1328 | args: Parsed command line arguments
1329 | system_info: System information dict
1330 | gpu_info: GPU information dict
1331 | env: Environment variables dict
1332 |
1333 | Returns:
1334 | tuple: (using_homebrew_pytorch: bool, pytorch_installed: bool, env: dict)
1335 | """
1336 | # Check for Homebrew PyTorch installation
1337 | using_homebrew_pytorch = False
1338 | if system_info.get("has_homebrew_pytorch"):
1339 | print_info(f"Using existing Homebrew PyTorch installation (version: {system_info.get('homebrew_pytorch_version')})")
1340 | using_homebrew_pytorch = True
1341 | # Set the environment variable to use ONNX for embeddings
1342 | env['MCP_MEMORY_USE_ONNX'] = '1'
1343 | pytorch_installed = True
1344 | else:
1345 | # Handle platform-specific PyTorch installation
1346 | pytorch_installed = install_pytorch_platform_specific(system_info, gpu_info, args)
1347 | if not pytorch_installed:
1348 | print_warning("Platform-specific PyTorch installation failed, but will continue with package installation")
1349 |
1350 | return using_homebrew_pytorch, pytorch_installed, env
1351 |
1352 |
1353 | def _should_use_onnx_installation(system_info, args, using_homebrew_pytorch):
1354 | """Determine if ONNX installation path should be used.
1355 |
1356 | Args:
1357 | system_info: System information dict
1358 | args: Parsed command line arguments
1359 | using_homebrew_pytorch: Whether Homebrew PyTorch is being used
1360 |
1361 | Returns:
1362 | bool: True if ONNX installation path should be used
1363 | """
1364 | return (system_info["is_macos"] and system_info["is_x86"] and
1365 | (sys.version_info >= (3, 13) or using_homebrew_pytorch or args.skip_pytorch))
1366 |
1367 |
1368 | def _install_with_onnx(installer_cmd, install_mode, chosen_backend, env, using_homebrew_pytorch):
1369 | """Install using SQLite-vec + ONNX configuration.
1370 |
1371 | Args:
1372 | installer_cmd: Command prefix for installer
1373 | install_mode: Installation mode flags (-e for dev mode)
1374 | chosen_backend: Storage backend name
1375 | env: Environment variables
1376 | using_homebrew_pytorch: Whether using Homebrew PyTorch
1377 |
1378 | Returns:
1379 | bool: True if installation succeeded, False otherwise
1380 | """
1381 | try:
1382 | # Print installation context
1383 | if using_homebrew_pytorch:
1384 | print_info("Using Homebrew PyTorch - installing with SQLite-vec + ONNX configuration")
1385 | elif install_mode:
1386 | print_info("Skipping PyTorch installation - using SQLite-vec + ONNX configuration")
1387 | else:
1388 | print_info("Using Python 3.13+ on macOS Intel - using SQLite-vec + ONNX configuration")
1389 |
1390 | # Install without ML dependencies
1391 | cmd = installer_cmd + ['install', '--no-deps'] + install_mode + ['.']
1392 | print_info(f"Running: {' '.join(cmd)}")
1393 | subprocess.check_call(cmd, env=env)
1394 |
1395 | # Install core dependencies except torch/sentence-transformers
1396 | print_info("Installing core dependencies (without ML libraries for compatibility)...")
1397 | print_info("Note: First run will download embedding models automatically (~25MB)")
1398 |
1399 | # Build dependency list
1400 | dependencies = [
1401 | "mcp>=1.0.0,<2.0.0",
1402 | "onnxruntime>=1.14.1", # ONNX runtime for embeddings
1403 | "tokenizers>=0.20.0", # Required for ONNX tokenization
1404 | "httpx>=0.24.0", # For downloading ONNX models
1405 | "aiohttp>=3.8.0" # Required for MCP server functionality
1406 | ]
1407 |
1408 | # Add backend-specific dependencies
1409 | if chosen_backend == "sqlite_vec":
1410 | dependencies.append("sqlite-vec>=0.1.0")
1411 | else:
1412 | dependencies.append("chromadb==0.5.23")
1413 | dependencies.append("tokenizers==0.20.3")
1414 |
1415 | # Install dependencies
1416 | subprocess.check_call([sys.executable, '-m', 'pip', 'install'] + dependencies)
1417 |
1418 | # Configure ONNX runtime
1419 | print_info("Configuring to use ONNX runtime for inference without PyTorch...")
1420 | env['MCP_MEMORY_USE_ONNX'] = '1'
1421 | os.environ['MCP_MEMORY_USE_ONNX'] = '1'
1422 |
1423 | # Switch to SQLite-vec if needed
1424 | if chosen_backend != "sqlite_vec":
1425 | print_info("Switching to SQLite-vec backend for better compatibility")
1426 | env['MCP_MEMORY_STORAGE_BACKEND'] = 'sqlite_vec'
1427 | os.environ['MCP_MEMORY_STORAGE_BACKEND'] = 'sqlite_vec'
1428 |
1429 | print_success("MCP Memory Service installed successfully (SQLite-vec + ONNX)")
1430 |
1431 | if using_homebrew_pytorch:
1432 | print_info("Using Homebrew PyTorch installation for embedding generation")
1433 | print_info("Environment configured to use SQLite-vec backend and ONNX runtime")
1434 | else:
1435 | print_warning("ML libraries (PyTorch/sentence-transformers) were not installed due to compatibility issues")
1436 | print_info("The service will use ONNX runtime for inference instead")
1437 |
1438 | return True
1439 | except subprocess.SubprocessError as e:
1440 | print_error(f"Failed to install with ONNX approach: {e}")
1441 | return False
1442 |
1443 |
1444 | def _install_standard(installer_cmd, install_mode, env):
1445 | """Perform standard pip/uv installation.
1446 |
1447 | Args:
1448 | installer_cmd: Command prefix for installer
1449 | install_mode: Installation mode flags (-e for dev mode)
1450 | env: Environment variables
1451 |
1452 | Returns:
1453 | bool: True if installation succeeded, False otherwise
1454 | """
1455 | try:
1456 | cmd = installer_cmd + ['install'] + install_mode + ['.']
1457 | print_info(f"Running: {' '.join(cmd)}")
1458 | subprocess.check_call(cmd, env=env)
1459 | print_success("MCP Memory Service installed successfully")
1460 | return True
1461 | except subprocess.SubprocessError as e:
1462 | print_error(f"Failed to install MCP Memory Service: {e}")
1463 | return False
1464 |
1465 |
1466 | def _handle_installation_failure(system_info):
1467 | """Provide helpful guidance when installation fails.
1468 |
1469 | Args:
1470 | system_info: System information dict
1471 | """
1472 | # Special handling for macOS with compatibility issues
1473 | if system_info["is_macos"] and system_info["is_x86"]:
1474 | print_warning("Installation on macOS Intel is challenging")
1475 | print_info("Try manually installing with:")
1476 | print_info("1. pip install --no-deps .")
1477 | print_info("2. pip install sqlite-vec>=0.1.0 mcp>=1.0.0,<2.0.0 onnxruntime>=1.14.1 aiohttp>=3.8.0")
1478 | print_info("3. export MCP_MEMORY_USE_ONNX=1")
1479 | print_info("4. export MCP_MEMORY_STORAGE_BACKEND=sqlite_vec")
1480 |
1481 | if system_info.get("has_homebrew_pytorch"):
1482 | print_info("Homebrew PyTorch was detected but installation still failed.")
1483 | print_info("Try running: python install.py --storage-backend sqlite_vec --skip-pytorch")
1484 |
1485 |
1486 | def install_package(args):
1487 | """Install the package with the appropriate dependencies, supporting pip or uv."""
1488 | print_step("3", "Installing MCP Memory Service")
1489 |
1490 | # Determine installation mode
1491 | install_mode = []
1492 | if args.dev:
1493 | install_mode = ['-e']
1494 | print_info("Installing in development mode")
1495 |
1496 | # Setup installer command (pip or uv)
1497 | installer_cmd, uv_path = _setup_installer_command()
1498 | if installer_cmd is None:
1499 | return False
1500 |
1501 | # Configure storage backend and GPU
1502 | env, system_info, gpu_info, chosen_backend = _configure_storage_and_gpu(args)
1503 | if env is None:
1504 | return False
1505 |
1506 | # Handle PyTorch setup
1507 | using_homebrew_pytorch, pytorch_installed, env = _handle_pytorch_setup(
1508 | args, system_info, gpu_info, env
1509 | )
1510 |
1511 | # Determine installation path
1512 | if _should_use_onnx_installation(system_info, args, using_homebrew_pytorch):
1513 | # Use ONNX-based installation for macOS Intel with Python 3.13+ or Homebrew PyTorch
1514 | success = _install_with_onnx(installer_cmd, install_mode, chosen_backend, env, using_homebrew_pytorch)
1515 | if success:
1516 | return True
1517 | # Fall through to standard installation if ONNX approach fails
1518 |
1519 | # Standard installation path
1520 | success = _install_standard(installer_cmd, install_mode, env)
1521 | if not success:
1522 | _handle_installation_failure(system_info)
1523 |
1524 | return success
1525 |
1526 | def configure_paths(args):
1527 | """Configure paths for the MCP Memory Service."""
1528 | print_step("4", "Configuring paths")
1529 |
1530 | # Get system info
1531 | system_info = detect_system()
1532 |
1533 | # Determine home directory
1534 | home_dir = Path.home()
1535 |
1536 | # Determine base directory based on platform
1537 | if platform.system() == 'Darwin': # macOS
1538 | base_dir = home_dir / 'Library' / 'Application Support' / 'mcp-memory'
1539 | elif platform.system() == 'Windows': # Windows
1540 | base_dir = Path(os.environ.get('LOCALAPPDATA', '')) / 'mcp-memory'
1541 | else: # Linux and others
1542 | base_dir = home_dir / '.local' / 'share' / 'mcp-memory'
1543 |
1544 | # Create directories based on storage backend
1545 | storage_backend = args.storage_backend or os.environ.get('MCP_MEMORY_STORAGE_BACKEND', 'sqlite_vec')
1546 |
1547 | if storage_backend == 'sqlite_vec':
1548 | # For sqlite-vec, we need a database file path
1549 | storage_path = args.chroma_path or (base_dir / 'sqlite_vec.db')
1550 | storage_dir = storage_path.parent if storage_path.name.endswith('.db') else storage_path
1551 | backups_path = args.backups_path or (base_dir / 'backups')
1552 |
1553 | try:
1554 | os.makedirs(storage_dir, exist_ok=True)
1555 | os.makedirs(backups_path, exist_ok=True)
1556 | print_info(f"SQLite-vec database: {storage_path}")
1557 | print_info(f"Backups path: {backups_path}")
1558 |
1559 | # Test if directory is writable
1560 | test_file = os.path.join(storage_dir, '.write_test')
1561 | with open(test_file, 'w') as f:
1562 | f.write('test')
1563 | os.remove(test_file)
1564 |
1565 | # Initialize the SQLite-vec database
1566 | if not initialize_sqlite_vec_database(storage_path):
1567 | print_warning("SQLite-vec database initialization failed, but continuing...")
1568 |
1569 | except Exception as e:
1570 | print_error(f"Failed to configure SQLite-vec paths: {e}")
1571 | return False
1572 | else:
1573 | # ChromaDB configuration
1574 | chroma_path = args.chroma_path or (base_dir / 'chroma_db')
1575 | backups_path = args.backups_path or (base_dir / 'backups')
1576 | storage_path = chroma_path
1577 |
1578 | try:
1579 | os.makedirs(chroma_path, exist_ok=True)
1580 | os.makedirs(backups_path, exist_ok=True)
1581 | print_info(f"ChromaDB path: {chroma_path}")
1582 | print_info(f"Backups path: {backups_path}")
1583 |
1584 | # Test if directories are writable
1585 | test_file = os.path.join(chroma_path, '.write_test')
1586 | with open(test_file, 'w') as f:
1587 | f.write('test')
1588 | os.remove(test_file)
1589 | except Exception as e:
1590 | print_error(f"Failed to configure ChromaDB paths: {e}")
1591 | return False
1592 |
1593 | # Test backups directory for both backends
1594 | try:
1595 | test_file = os.path.join(backups_path, '.write_test')
1596 | with open(test_file, 'w') as f:
1597 | f.write('test')
1598 | os.remove(test_file)
1599 | print_success("Storage directories created and are writable")
1600 | except Exception as e:
1601 | print_error(f"Failed to test backups directory: {e}")
1602 | return False
1603 |
1604 | # Configure Claude Desktop if available
1605 | claude_config_paths = [
1606 | home_dir / 'Library' / 'Application Support' / 'Claude' / 'claude_desktop_config.json',
1607 | home_dir / '.config' / 'Claude' / 'claude_desktop_config.json',
1608 | Path('claude_config') / 'claude_desktop_config.json'
1609 | ]
1610 |
1611 | for config_path in claude_config_paths:
1612 | if config_path.exists():
1613 | print_info(f"Found Claude Desktop config at {config_path}")
1614 | try:
1615 | import json
1616 | with open(config_path, 'r') as f:
1617 | config = json.load(f)
1618 |
1619 | # Update or add MCP Memory configuration
1620 | if 'mcpServers' not in config:
1621 | config['mcpServers'] = {}
1622 |
1623 | # Create environment configuration based on storage backend
1624 | env_config = {
1625 | "MCP_MEMORY_BACKUPS_PATH": str(backups_path),
1626 | "MCP_MEMORY_STORAGE_BACKEND": storage_backend
1627 | }
1628 |
1629 | if storage_backend == 'sqlite_vec':
1630 | env_config["MCP_MEMORY_SQLITE_PATH"] = str(storage_path)
1631 | else:
1632 | env_config["MCP_MEMORY_CHROMA_PATH"] = str(storage_path)
1633 |
1634 | # Create or update the memory server configuration
1635 | if system_info["is_windows"]:
1636 | # Use the memory_wrapper.py script for Windows
1637 | script_path = os.path.abspath("memory_wrapper.py")
1638 | config['mcpServers']['memory'] = {
1639 | "command": "python",
1640 | "args": [script_path],
1641 | "env": env_config
1642 | }
1643 | print_info("Configured Claude Desktop to use memory_wrapper.py for Windows")
1644 | else:
1645 | # Use the standard configuration for other platforms
1646 | config['mcpServers']['memory'] = {
1647 | "command": UV_EXECUTABLE_PATH or "uv",
1648 | "args": [
1649 | "--directory",
1650 | os.path.abspath("."),
1651 | "run",
1652 | "memory"
1653 | ],
1654 | "env": env_config
1655 | }
1656 |
1657 | with open(config_path, 'w') as f:
1658 | json.dump(config, f, indent=2)
1659 |
1660 | print_success("Updated Claude Desktop configuration")
1661 | except Exception as e:
1662 | print_warning(f"Failed to update Claude Desktop configuration: {e}")
1663 | break
1664 |
1665 | return True
1666 |
1667 | def verify_installation():
1668 | """Verify the installation."""
1669 | print_step("5", "Verifying installation")
1670 |
1671 | # Get system info
1672 | system_info = detect_system()
1673 |
1674 | # Check if the package is installed
1675 | try:
1676 | import mcp_memory_service
1677 | print_success(f"MCP Memory Service is installed: {mcp_memory_service.__file__}")
1678 | except ImportError:
1679 | print_error("MCP Memory Service is not installed correctly")
1680 | return False
1681 |
1682 | # Check if the entry point is available
1683 | memory_script = shutil.which('memory')
1684 | if memory_script:
1685 | print_success(f"Memory command is available: {memory_script}")
1686 | else:
1687 | print_warning("Memory command is not available in PATH")
1688 |
1689 | # Check storage backend installation
1690 | storage_backend = os.environ.get('MCP_MEMORY_STORAGE_BACKEND', 'sqlite_vec')
1691 |
1692 | if storage_backend == 'sqlite_vec':
1693 | try:
1694 | import sqlite_vec
1695 | print_success(f"SQLite-vec is installed: {sqlite_vec.__version__}")
1696 | except ImportError:
1697 | print_error("SQLite-vec is not installed correctly")
1698 | return False
1699 | elif storage_backend == 'chromadb':
1700 | try:
1701 | import chromadb
1702 | print_success(f"ChromaDB is installed: {chromadb.__version__}")
1703 | except ImportError:
1704 | print_error("ChromaDB is not installed correctly")
1705 | return False
1706 |
1707 | # Check for ONNX runtime
1708 | try:
1709 | import onnxruntime
1710 | print_success(f"ONNX Runtime is installed: {onnxruntime.__version__}")
1711 | use_onnx = os.environ.get('MCP_MEMORY_USE_ONNX', '').lower() in ('1', 'true', 'yes')
1712 | if use_onnx:
1713 | print_info("Environment configured to use ONNX runtime for embeddings")
1714 | # Check for tokenizers (required for ONNX)
1715 | try:
1716 | import tokenizers
1717 | print_success(f"Tokenizers is installed: {tokenizers.__version__}")
1718 | except ImportError:
1719 | print_warning("Tokenizers not installed but required for ONNX embeddings")
1720 | print_info("Install with: pip install tokenizers>=0.20.0")
1721 | except ImportError:
1722 | print_warning("ONNX Runtime is not installed. This is recommended for PyTorch-free operation.")
1723 | print_info("Install with: pip install onnxruntime>=1.14.1 tokenizers>=0.20.0")
1724 |
1725 | # Check for Homebrew PyTorch
1726 | homebrew_pytorch = False
1727 | if system_info.get("has_homebrew_pytorch"):
1728 | homebrew_pytorch = True
1729 | print_success(f"Homebrew PyTorch detected: {system_info.get('homebrew_pytorch_version')}")
1730 | print_info("Using system-installed PyTorch instead of pip version")
1731 |
1732 | # Check ML dependencies as optional
1733 | pytorch_installed = False
1734 | try:
1735 | import torch
1736 | pytorch_installed = True
1737 | print_info(f"PyTorch is installed: {torch.__version__}")
1738 |
1739 | # Check for CUDA
1740 | if torch.cuda.is_available():
1741 | print_success(f"CUDA is available: {torch.version.cuda}")
1742 | print_info(f"GPU: {torch.cuda.get_device_name(0)}")
1743 | # Check for MPS (Apple Silicon)
1744 | elif hasattr(torch.backends, 'mps') and torch.backends.mps.is_available():
1745 | print_success("MPS (Metal Performance Shaders) is available")
1746 | # Check for DirectML
1747 | else:
1748 | try:
1749 | import torch_directml
1750 | version = getattr(torch_directml, '__version__', 'Unknown version')
1751 | print_success(f"DirectML is available: {version}")
1752 | except ImportError:
1753 | print_info("Using CPU-only PyTorch")
1754 |
1755 | # For macOS Intel, verify compatibility with sentence-transformers
1756 | if system_info["is_macos"] and system_info["is_x86"]:
1757 | torch_version = torch.__version__.split('.')
1758 | major, minor = int(torch_version[0]), int(torch_version[1])
1759 |
1760 | print_info(f"Verifying torch compatibility on macOS Intel (v{major}.{minor})")
1761 | if major < 1 or (major == 1 and minor < 6):
1762 | print_warning(f"PyTorch version {torch.__version__} may be too old for sentence-transformers")
1763 | elif major > 2 or (major == 2 and minor > 1):
1764 | print_warning(f"PyTorch version {torch.__version__} may be too new for sentence-transformers 2.2.2")
1765 | print_info("If you encounter issues, try downgrading to torch 2.0.1")
1766 |
1767 | except ImportError:
1768 | print_warning("PyTorch is not installed via pip. This is okay for basic operation with SQLite-vec backend.")
1769 | if homebrew_pytorch:
1770 | print_info("Using Homebrew PyTorch installation instead of pip version")
1771 | else:
1772 | print_info("For full functionality including embedding generation, install with: pip install 'mcp-memory-service[ml]'")
1773 | pytorch_installed = False
1774 |
1775 | # Check if sentence-transformers is installed correctly (only if PyTorch is installed)
1776 | if pytorch_installed or homebrew_pytorch:
1777 | try:
1778 | import sentence_transformers
1779 | print_success(f"sentence-transformers is installed: {sentence_transformers.__version__}")
1780 |
1781 | if pytorch_installed:
1782 | # Verify compatibility between torch and sentence-transformers
1783 | st_version = sentence_transformers.__version__.split('.')
1784 | torch_version = torch.__version__.split('.')
1785 |
1786 | st_major, st_minor = int(st_version[0]), int(st_version[1])
1787 | torch_major, torch_minor = int(torch_version[0]), int(torch_version[1])
1788 |
1789 | # Specific compatibility check for macOS Intel
1790 | if system_info["is_macos"] and system_info["is_x86"]:
1791 | if st_major >= 3 and (torch_major < 1 or (torch_major == 1 and torch_minor < 11)):
1792 | print_warning(f"sentence-transformers {sentence_transformers.__version__} requires torch>=1.11.0")
1793 | print_info("This may cause runtime issues - consider downgrading sentence-transformers to 2.2.2")
1794 |
1795 | # Verify by trying to load a model (minimal test)
1796 | try:
1797 | print_info("Testing sentence-transformers model loading...")
1798 | test_model = sentence_transformers.SentenceTransformer('paraphrase-MiniLM-L3-v2')
1799 | print_success("Successfully loaded test model")
1800 | except Exception as e:
1801 | print_warning(f"Model loading test failed: {e}")
1802 | print_warning("There may be compatibility issues between PyTorch and sentence-transformers")
1803 |
1804 | except ImportError:
1805 | print_warning("sentence-transformers is not installed. This is okay for basic operation with SQLite-vec backend.")
1806 | print_info("For full functionality including embedding generation, install with: pip install 'mcp-memory-service[ml]'")
1807 |
1808 | # Check for SQLite-vec + ONNX configuration
1809 | if storage_backend == 'sqlite_vec' and os.environ.get('MCP_MEMORY_USE_ONNX', '').lower() in ('1', 'true', 'yes'):
1810 | print_success("SQLite-vec + ONNX configuration is set up correctly")
1811 | print_info("This configuration can run without PyTorch dependency")
1812 |
1813 | try:
1814 | # Import the key components to verify installation
1815 | from mcp_memory_service.storage.sqlite_vec import SqliteVecMemoryStorage
1816 | from mcp_memory_service.models.memory import Memory
1817 | print_success("SQLite-vec + ONNX components loaded successfully")
1818 |
1819 | # Check paths
1820 | sqlite_path = os.environ.get('MCP_MEMORY_SQLITE_PATH', '')
1821 | if sqlite_path:
1822 | print_info(f"SQLite-vec database path: {sqlite_path}")
1823 | else:
1824 | print_warning("MCP_MEMORY_SQLITE_PATH is not set")
1825 |
1826 | backups_path = os.environ.get('MCP_MEMORY_BACKUPS_PATH', '')
1827 | if backups_path:
1828 | print_info(f"Backups path: {backups_path}")
1829 | else:
1830 | print_warning("MCP_MEMORY_BACKUPS_PATH is not set")
1831 |
1832 | except ImportError as e:
1833 | print_error(f"Failed to import SQLite-vec components: {e}")
1834 | return False
1835 |
1836 | # Check if MCP Memory Service package is installed correctly
1837 | try:
1838 | import mcp_memory_service
1839 | print_success(f"MCP Memory Service is installed correctly")
1840 | return True
1841 | except ImportError:
1842 | print_error("MCP Memory Service is not installed correctly")
1843 | return False
1844 |
1845 | def is_legacy_hardware(system_info):
1846 | """Detect legacy hardware that needs special handling."""
1847 | if system_info["is_macos"] and system_info["is_x86"]:
1848 | # Check if it's likely an older Intel Mac
1849 | # This is a heuristic based on common patterns
1850 | try:
1851 | # Try to get more detailed system info
1852 | print_info("Detecting hardware configuration (this may take a moment)...")
1853 | result = subprocess.run(
1854 | ['system_profiler', 'SPHardwareDataType'],
1855 | capture_output=True, text=True, timeout=30
1856 | )
1857 | if result.returncode == 0:
1858 | output = result.stdout.lower()
1859 | # Look for indicators of older hardware
1860 | if any(year in output for year in ['2013', '2014', '2015', '2016', '2017']):
1861 | return True
1862 | except (subprocess.SubprocessError, subprocess.TimeoutExpired):
1863 | pass
1864 |
1865 | return False
1866 |
1867 | def detect_memory_gb():
1868 | """Detect available system memory in GB."""
1869 | try:
1870 | import psutil
1871 | return psutil.virtual_memory().total / (1024**3)
1872 | except ImportError:
1873 | # Fallback detection methods
1874 | try:
1875 | if platform.system() == "Darwin": # macOS
1876 | result = subprocess.run(
1877 | ['sysctl', '-n', 'hw.memsize'],
1878 | capture_output=True, text=True
1879 | )
1880 | if result.returncode == 0:
1881 | return int(result.stdout.strip()) / (1024**3)
1882 | elif platform.system() == "Linux":
1883 | with open('/proc/meminfo', 'r') as f:
1884 | for line in f:
1885 | if line.startswith('MemTotal:'):
1886 | return int(line.split()[1]) / (1024**2)
1887 | except (subprocess.SubprocessError, FileNotFoundError, IOError):
1888 | pass
1889 |
1890 | return 0 # Unknown
1891 |
1892 | def recommend_backend_intelligent(system_info, gpu_info, memory_gb, args):
1893 | """Intelligent backend recommendation based on hardware analysis."""
1894 | # User explicitly chose backend
1895 | if hasattr(args, 'storage_backend') and args.storage_backend:
1896 | return args.storage_backend
1897 |
1898 | # Legacy hardware mode
1899 | if args.legacy_hardware or is_legacy_hardware(system_info):
1900 | print_info("[DETECT] Legacy hardware detected - optimizing for compatibility")
1901 | return "sqlite_vec"
1902 |
1903 | # Server mode
1904 | if args.server_mode:
1905 | print_info("[SERVER] Server mode - selecting lightweight backend")
1906 | return "sqlite_vec"
1907 |
1908 | # Low memory systems
1909 | if memory_gb > 0 and memory_gb < 4:
1910 | print_info(f"[MEMORY] Limited memory detected ({memory_gb:.1f}GB) - using efficient backend")
1911 | return "sqlite_vec"
1912 |
1913 | # macOS Intel with known ChromaDB issues
1914 | if system_info["is_macos"] and system_info["is_x86"]:
1915 | compatibility = detect_storage_backend_compatibility(system_info, gpu_info)
1916 | if compatibility["chromadb"]["recommendation"] == "problematic":
1917 | print_info("[WARNING] macOS Intel compatibility issues detected - using SQLite-vec")
1918 | # Set environment variables for consistent backend selection
1919 | os.environ['MCP_MEMORY_STORAGE_BACKEND'] = 'sqlite_vec'
1920 | # For Intel Macs, also enable ONNX runtime for better compatibility
1921 | if system_info.get("has_homebrew_pytorch") or sys.version_info >= (3, 13):
1922 | print_info("[CONFIG] Enabling ONNX runtime for better compatibility")
1923 | os.environ['MCP_MEMORY_USE_ONNX'] = '1'
1924 | return "sqlite_vec"
1925 |
1926 | # Hardware with GPU acceleration - SQLite-vec still recommended for simplicity
1927 | if gpu_info.get("has_cuda") or gpu_info.get("has_mps") or gpu_info.get("has_directml"):
1928 | gpu_type = "CUDA" if gpu_info.get("has_cuda") else "MPS" if gpu_info.get("has_mps") else "DirectML"
1929 | print_info(f"[GPU] {gpu_type} acceleration detected - SQLite-vec recommended for simplicity and speed")
1930 | return "sqlite_vec"
1931 |
1932 | # High memory systems without GPU - explain the choice
1933 | if memory_gb >= 16:
1934 | print_info("[CHOICE] High-memory system without GPU detected")
1935 | print_info(" -> SQLite-vec: Faster startup, simpler setup, no network dependencies")
1936 | print_info(" -> ChromaDB: Legacy option, being deprecated in v6.0.0")
1937 | print_info(" -> Defaulting to SQLite-vec (recommended for all users)")
1938 | return "sqlite_vec"
1939 |
1940 | # Default recommendation for most users
1941 | print_info("[DEFAULT] Recommending SQLite-vec for optimal user experience")
1942 | return "sqlite_vec"
1943 |
1944 | def show_detailed_help():
1945 | """Show detailed hardware-specific installation help."""
1946 | print_header("MCP Memory Service - Hardware-Specific Installation Guide")
1947 |
1948 | # Detect current system
1949 | system_info = detect_system()
1950 | gpu_info = detect_gpu()
1951 | memory_gb = detect_memory_gb()
1952 |
1953 | # Check SQLite extension support
1954 | extension_supported, extension_message = check_sqlite_extension_support()
1955 |
1956 | print_info("Your System Configuration:")
1957 | print_info(f" Platform: {platform.system()} {platform.release()}")
1958 | print_info(f" Architecture: {platform.machine()}")
1959 | print_info(f" Python: {sys.version_info.major}.{sys.version_info.minor}")
1960 | if memory_gb > 0:
1961 | print_info(f" Memory: {memory_gb:.1f}GB")
1962 | print_info(f" SQLite Extensions: {'✅ Supported' if extension_supported else '❌ Not Supported'}")
1963 |
1964 | # Warn about potential sqlite-vec issues
1965 | if not extension_supported and platform.system().lower() == "darwin":
1966 | print_warning("SQLite extension support not available - this may cause issues with sqlite-vec backend")
1967 | print_info("Consider using Homebrew Python: brew install python")
1968 |
1969 | # Hardware-specific recommendations
1970 | print_step("Recommendations", "Based on your hardware")
1971 |
1972 | if is_legacy_hardware(system_info):
1973 | print_success("Legacy Hardware Path (2013-2017 Intel Mac)")
1974 | print_info(" Recommended: python install.py --legacy-hardware")
1975 | print_info(" This will:")
1976 | print_info(" • Use SQLite-vec backend (avoids ChromaDB compatibility issues)")
1977 | print_info(" • Configure ONNX runtime for CPU-only inference")
1978 | print_info(" • Use Homebrew PyTorch for better compatibility")
1979 | print_info(" • Optimize resource usage for older hardware")
1980 | elif system_info["is_macos"] and system_info["is_arm"]:
1981 | print_success("Apple Silicon Mac - Modern Hardware Path")
1982 | print_info(" Recommended: python install.py")
1983 | print_info(" This will:")
1984 | print_info(" • Use SQLite-vec backend (fast and efficient)")
1985 | print_info(" • Enable MPS acceleration")
1986 | print_info(" • Zero network dependencies")
1987 | elif system_info["is_windows"] and gpu_info.get("has_cuda"):
1988 | print_success("Windows with CUDA GPU - High Performance Path")
1989 | print_info(" Recommended: python install.py")
1990 | print_info(" This will:")
1991 | print_info(" • Use SQLite-vec backend (fast and efficient)")
1992 | print_info(" • Enable CUDA acceleration")
1993 | print_info(" • Zero network dependencies")
1994 | elif memory_gb > 0 and memory_gb < 4:
1995 | print_success("Low-Memory System")
1996 | print_info(" Recommended: python install.py --storage-backend sqlite_vec")
1997 | print_info(" This will:")
1998 | print_info(" • Use lightweight SQLite-vec backend")
1999 | print_info(" • Minimize memory usage")
2000 | print_info(" • Enable ONNX runtime for efficiency")
2001 | elif memory_gb >= 16 and not (gpu_info.get("has_cuda") or gpu_info.get("has_mps") or gpu_info.get("has_directml")):
2002 | print_success("High-Memory System (No GPU) - Choose Your Path")
2003 | print_info(" Option 1 (Recommended): python install.py")
2004 | print_info(" • SQLite-vec: Fast startup, simple setup, same features")
2005 | print_info(" Option 2: python install.py --storage-backend chromadb")
2006 | print_info(" • ChromaDB: Better for 10K+ memories, production deployments")
2007 | print_info(" Most users benefit from SQLite-vec's simplicity")
2008 | elif gpu_info.get("has_cuda") or gpu_info.get("has_mps") or gpu_info.get("has_directml"):
2009 | gpu_type = "CUDA" if gpu_info.get("has_cuda") else "MPS" if gpu_info.get("has_mps") else "DirectML"
2010 | print_success(f"GPU-Accelerated System ({gpu_type}) - High Performance Path")
2011 | print_info(" Recommended: python install.py")
2012 | print_info(" This will:")
2013 | print_info(f" • Use SQLite-vec backend (fast and efficient)")
2014 | print_info(f" • Enable {gpu_type} hardware acceleration")
2015 | print_info(" • Zero network dependencies")
2016 | else:
2017 | print_success("Standard Installation")
2018 | print_info(" Recommended: python install.py")
2019 | print_info(" This will:")
2020 | print_info(" • Use SQLite-vec backend (optimal for most users)")
2021 | print_info(" • Fast startup and simple setup")
2022 | print_info(" • Full semantic search capabilities")
2023 |
2024 | print_step("Available Options", "Command-line flags you can use")
2025 | print_info(" --legacy-hardware : Optimize for 2013-2017 Intel Macs")
2026 | print_info(" --server-mode : Headless server installation")
2027 | print_info(" --storage-backend X : Force backend (chromadb/sqlite_vec)")
2028 | print_info(" --enable-http-api : Enable HTTP/SSE web interface")
2029 | print_info(" --use-homebrew-pytorch: Use existing Homebrew PyTorch")
2030 |
2031 | print_step("Documentation", "Hardware-specific guides")
2032 | print_info(" Legacy Mac Guide: docs/platforms/macos-intel-legacy.md")
2033 | print_info(" Backend Comparison: docs/guides/STORAGE_BACKENDS.md")
2034 | print_info(" Master Guide: docs/guides/INSTALLATION_MASTER.md")
2035 |
2036 | def generate_personalized_docs():
2037 | """Generate personalized setup documentation."""
2038 | print_header("Generating Personalized Setup Guide")
2039 |
2040 | # Detect system
2041 | system_info = detect_system()
2042 | gpu_info = detect_gpu()
2043 | memory_gb = detect_memory_gb()
2044 |
2045 | # Create personalized guide
2046 | guide_content = f"""# Your Personalized MCP Memory Service Setup Guide
2047 |
2048 | Generated on: {platform.node()} at {__import__('datetime').datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
2049 |
2050 | ## Your System Configuration
2051 |
2052 | - **Platform**: {platform.system()} {platform.release()}
2053 | - **Architecture**: {platform.machine()}
2054 | - **Python Version**: {sys.version_info.major}.{sys.version_info.minor}
2055 | - **Memory**: {memory_gb:.1f}GB (detected)
2056 | - **GPU**: {'Yes (' + ('CUDA' if gpu_info.get('has_cuda') else 'MPS' if gpu_info.get('has_mps') else 'DirectML' if gpu_info.get('has_directml') else 'Unknown') + ')' if gpu_info.get('has_cuda') or gpu_info.get('has_mps') or gpu_info.get('has_directml') else 'No'}
2057 |
2058 | ## Recommended Installation Command
2059 |
2060 | ```bash
2061 | """
2062 |
2063 | # Generate recommendation
2064 | class Args:
2065 | storage_backend = None
2066 | legacy_hardware = False
2067 | server_mode = False
2068 |
2069 | args = Args()
2070 | recommended_backend = recommend_backend_intelligent(system_info, gpu_info, memory_gb, args)
2071 |
2072 | if is_legacy_hardware(system_info):
2073 | guide_content += "python install.py --legacy-hardware\n"
2074 | elif memory_gb < 4:
2075 | guide_content += "python install.py --storage-backend sqlite_vec\n"
2076 | else:
2077 | guide_content += "python install.py\n"
2078 |
2079 | guide_content += f"""```
2080 |
2081 | ## Why This Configuration?
2082 |
2083 | Based on your {platform.system()} system with {memory_gb:.1f}GB RAM:
2084 | """
2085 |
2086 | if is_legacy_hardware(system_info):
2087 | guide_content += """
2088 | - [OK] **Hardware Compatibility**: SQLite-vec avoids ChromaDB installation issues on older Intel Macs
2089 | - [OK] **Homebrew PyTorch**: Better compatibility with older systems and reduced dependencies
2090 | - [OK] **ONNX Runtime**: CPU-optimized inference for systems without GPU acceleration
2091 | - [OK] **Memory Efficient**: Optimized resource usage for systems with limited RAM
2092 | - [OK] **Full Feature Set**: Complete semantic search, tagging, and time-based recall capabilities
2093 | """
2094 | elif recommended_backend == "sqlite_vec":
2095 | if memory_gb >= 16 and not (gpu_info.get("has_cuda") or gpu_info.get("has_mps") or gpu_info.get("has_directml")):
2096 | guide_content += """
2097 | - [OK] **Smart Choice**: SQLite-vec recommended for high-memory systems without GPU
2098 | - [OK] **No GPU Needed**: ChromaDB's advantages require GPU acceleration you don't have
2099 | - [OK] **Faster Startup**: Database ready in 2-3 seconds vs ChromaDB's 15-30 seconds
2100 | - [OK] **Simpler Setup**: Single-file database, no complex dependencies
2101 | - [OK] **Full Feature Set**: Complete semantic search, tagging, and time-based recall capabilities
2102 | - [INFO] **Alternative**: Use `--storage-backend chromadb` if you plan 10K+ memories
2103 | """
2104 | else:
2105 | guide_content += """
2106 | - [OK] **SQLite-vec Backend**: Lightweight with complete vector search capabilities
2107 | - [OK] **Low Memory Usage**: Optimized for systems with limited RAM
2108 | - [OK] **Quick Startup**: Database ready in seconds
2109 | - [OK] **Full Feature Set**: Semantic search, tagging, time-based recall
2110 | """
2111 | else:
2112 | guide_content += """
2113 | - [OK] **ChromaDB Backend**: Production-grade with advanced HNSW indexing and rich ecosystem
2114 | - [OK] **Hardware Acceleration**: Takes advantage of your GPU/MPS acceleration
2115 | - [OK] **Scalable Performance**: Optimized for large datasets (10K+ memories) and complex metadata queries
2116 | - [OK] **Full Feature Set**: Complete semantic search, tagging, and time-based recall capabilities
2117 | """
2118 |
2119 | guide_content += f"""
2120 | ## Next Steps
2121 |
2122 | 1. **Run the installation**:
2123 | ```bash
2124 | cd mcp-memory-service
2125 | {guide_content.split('```bash')[1].split('```')[0].strip()}
2126 | ```
2127 |
2128 | 2. **Test the installation**:
2129 | ```bash
2130 | python scripts/test_memory_simple.py
2131 | ```
2132 |
2133 | 3. **Configure Claude Desktop**:
2134 | The installer will generate the optimal configuration for your system.
2135 |
2136 | ## Troubleshooting
2137 |
2138 | If you encounter issues, see the platform-specific guide:
2139 | - **Legacy Mac Issues**: docs/platforms/macos-intel-legacy.md
2140 | - **General Issues**: docs/guides/troubleshooting.md
2141 | - **Backend Selection**: docs/guides/STORAGE_BACKENDS.md
2142 |
2143 | ## Support
2144 |
2145 | Generated configuration ID: {hash(str(system_info) + str(gpu_info))}-{int(__import__('time').time())}
2146 | Include this ID when requesting support for faster assistance.
2147 | """
2148 |
2149 | # Write the guide
2150 | guide_path = "YOUR_PERSONALIZED_SETUP_GUIDE.md"
2151 | with open(guide_path, 'w') as f:
2152 | f.write(guide_content)
2153 |
2154 | print_success(f"Personalized setup guide created: {guide_path}")
2155 | print_info("This guide contains hardware-specific recommendations for your system")
2156 | print_info("Keep this file for future reference and troubleshooting")
2157 |
2158 | def configure_claude_code_integration(system_info):
2159 | """Configure Claude Code MCP integration with optimized settings."""
2160 | print_step("6", "Configuring Claude Code Integration")
2161 |
2162 | # Check if Claude Code is installed
2163 | try:
2164 | result = subprocess.run(['claude', '--version'], capture_output=True, text=True, timeout=5)
2165 | if result.returncode != 0:
2166 | print_warning("Claude Code CLI not found. Please install it first:")
2167 | print_info("curl -fsSL https://claude.ai/install.sh | sh")
2168 | return False
2169 | except (subprocess.SubprocessError, FileNotFoundError, subprocess.TimeoutExpired):
2170 | print_warning("Claude Code CLI not found. Please install it first:")
2171 | print_info("curl -fsSL https://claude.ai/install.sh | sh")
2172 | return False
2173 |
2174 | print_success("Claude Code CLI detected")
2175 |
2176 | # Load template and create personalized .mcp.json
2177 | template_path = Path('.mcp.json.template')
2178 | if not template_path.exists():
2179 | print_error("Template file .mcp.json.template not found")
2180 | return False
2181 |
2182 | try:
2183 | import json
2184 | with open(template_path, 'r') as f:
2185 | template_content = f.read()
2186 |
2187 | # Replace placeholders with actual values
2188 | user_home = str(Path.home())
2189 | personalized_content = template_content.replace('{{USER_HOME}}', user_home)
2190 |
2191 | # Create .mcp.json
2192 | mcp_config_path = Path('.mcp.json')
2193 | with open(mcp_config_path, 'w') as f:
2194 | f.write(personalized_content)
2195 |
2196 | print_success(f"Created personalized .mcp.json configuration")
2197 | print_info(f"Configuration file: {mcp_config_path.absolute()}")
2198 |
2199 | # Add to .gitignore if it exists
2200 | gitignore_path = Path('.gitignore')
2201 | if gitignore_path.exists():
2202 | with open(gitignore_path, 'r') as f:
2203 | gitignore_content = f.read()
2204 |
2205 | if '.mcp.json' not in gitignore_content:
2206 | with open(gitignore_path, 'a') as f:
2207 | f.write('\n# MCP configuration (contains personal paths)\n.mcp.json\n')
2208 | print_success("Added .mcp.json to .gitignore")
2209 |
2210 | # Verify Claude Code can see the configuration
2211 | try:
2212 | result = subprocess.run(['claude', 'mcp', 'list'],
2213 | capture_output=True, text=True, timeout=10, cwd='.')
2214 | if 'memory-service' in result.stdout:
2215 | print_success("Claude Code MCP integration configured successfully!")
2216 | print_info("You can now use memory functions in Claude Code")
2217 | else:
2218 | print_warning("Configuration created but memory-service not detected")
2219 | print_info("You may need to restart Claude Code or check the configuration")
2220 | except (subprocess.SubprocessError, subprocess.TimeoutExpired):
2221 | print_warning("Could not verify Claude Code configuration")
2222 | print_info("Configuration file created - restart Claude Code to use memory functions")
2223 |
2224 | return True
2225 |
2226 | except Exception as e:
2227 | print_error(f"Failed to configure Claude Code integration: {e}")
2228 | return False
2229 |
2230 | def detect_mcp_clients():
2231 | """Detect installed MCP-compatible applications."""
2232 | clients = {}
2233 |
2234 | # Check for Claude Desktop
2235 | claude_config_paths = [
2236 | Path.home() / "AppData" / "Roaming" / "Claude" / "claude_desktop_config.json", # Windows
2237 | Path.home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json", # macOS
2238 | Path.home() / ".config" / "Claude" / "claude_desktop_config.json" # Linux
2239 | ]
2240 | for path in claude_config_paths:
2241 | if path.exists():
2242 | clients['claude_desktop'] = path
2243 | break
2244 |
2245 | # Check for Claude Code CLI
2246 | try:
2247 | result = subprocess.run(['claude', '--version'], capture_output=True, text=True, timeout=5)
2248 | if result.returncode == 0:
2249 | clients['claude_code'] = True
2250 | except (subprocess.SubprocessError, FileNotFoundError, subprocess.TimeoutExpired):
2251 | pass
2252 |
2253 | # Check for VS Code with MCP extension
2254 | vscode_settings_paths = [
2255 | Path.home() / "AppData" / "Roaming" / "Code" / "User" / "settings.json", # Windows
2256 | Path.home() / "Library" / "Application Support" / "Code" / "User" / "settings.json", # macOS
2257 | Path.home() / ".config" / "Code" / "User" / "settings.json" # Linux
2258 | ]
2259 | for path in vscode_settings_paths:
2260 | if path.exists():
2261 | try:
2262 | import json
2263 | with open(path, 'r') as f:
2264 | settings = json.load(f)
2265 | # Check for MCP-related extensions or configurations
2266 | if any('mcp' in str(key).lower() or 'model-context-protocol' in str(key).lower()
2267 | for key in settings.keys()):
2268 | clients['vscode_mcp'] = path
2269 | break
2270 | except (json.JSONDecodeError, IOError):
2271 | pass
2272 |
2273 | # Check for Continue IDE
2274 | continue_paths = [
2275 | Path.home() / ".continue" / "config.json",
2276 | Path.home() / ".config" / "continue" / "config.json",
2277 | Path.home() / "AppData" / "Roaming" / "continue" / "config.json" # Windows
2278 | ]
2279 | for path in continue_paths:
2280 | if path.exists():
2281 | clients['continue'] = path
2282 | break
2283 |
2284 | # Check for generic MCP configurations
2285 | generic_mcp_paths = [
2286 | Path.home() / ".mcp.json",
2287 | Path.home() / ".config" / "mcp" / "config.json",
2288 | Path.cwd() / ".mcp.json"
2289 | ]
2290 | for path in generic_mcp_paths:
2291 | if path.exists():
2292 | clients['generic_mcp'] = path
2293 | break
2294 |
2295 | # Check for Cursor IDE (similar to VS Code)
2296 | cursor_settings_paths = [
2297 | Path.home() / "AppData" / "Roaming" / "Cursor" / "User" / "settings.json", # Windows
2298 | Path.home() / "Library" / "Application Support" / "Cursor" / "User" / "settings.json", # macOS
2299 | Path.home() / ".config" / "Cursor" / "User" / "settings.json" # Linux
2300 | ]
2301 | for path in cursor_settings_paths:
2302 | if path.exists():
2303 | try:
2304 | import json
2305 | with open(path, 'r') as f:
2306 | settings = json.load(f)
2307 | # Check for MCP-related configurations
2308 | if any('mcp' in str(key).lower() or 'model-context-protocol' in str(key).lower()
2309 | for key in settings.keys()):
2310 | clients['cursor'] = path
2311 | break
2312 | except (json.JSONDecodeError, IOError):
2313 | pass
2314 |
2315 | return clients
2316 |
2317 | def print_detected_clients(clients):
2318 | """Print information about detected MCP clients."""
2319 | if clients:
2320 | print_info("Detected MCP Clients:")
2321 | for client_type, config_path in clients.items():
2322 | client_names = {
2323 | 'claude_desktop': 'Claude Desktop',
2324 | 'claude_code': 'Claude Code CLI',
2325 | 'vscode_mcp': 'VS Code with MCP',
2326 | 'continue': 'Continue IDE',
2327 | 'cursor': 'Cursor IDE',
2328 | 'generic_mcp': 'Generic MCP Client'
2329 | }
2330 | client_name = client_names.get(client_type, client_type.title())
2331 | config_info = config_path if isinstance(config_path, (str, Path)) else "CLI detected"
2332 | print_info(f" [*] {client_name}: {config_info}")
2333 | else:
2334 | print_info("No MCP clients detected - configuration will work with any future MCP client")
2335 |
2336 | def should_offer_multi_client_setup(args, final_backend):
2337 | """Determine if multi-client setup should be offered."""
2338 | # Only offer if using SQLite-vec backend (requirement for multi-client)
2339 | if final_backend != "sqlite_vec":
2340 | return False
2341 |
2342 | # Don't offer in pure server mode
2343 | if args.server_mode:
2344 | return False
2345 |
2346 | # Skip if user explicitly requested to skip
2347 | if args.skip_multi_client_prompt:
2348 | return False
2349 |
2350 | # Always beneficial for development environments - any future MCP client can benefit
2351 | return True
2352 |
2353 | def configure_detected_clients(clients, system_info, storage_backend="sqlite_vec"):
2354 | """Configure each detected client for multi-client access."""
2355 | success_count = 0
2356 |
2357 | for client_type, config_path in clients.items():
2358 | try:
2359 | if client_type == 'claude_desktop':
2360 | if configure_claude_desktop_multi_client(config_path, system_info, storage_backend):
2361 | success_count += 1
2362 | elif client_type == 'vscode_mcp' or client_type == 'cursor':
2363 | if configure_vscode_like_multi_client(config_path, client_type, storage_backend):
2364 | success_count += 1
2365 | elif client_type == 'continue':
2366 | if configure_continue_multi_client(config_path, storage_backend):
2367 | success_count += 1
2368 | elif client_type == 'generic_mcp':
2369 | if configure_generic_mcp_multi_client(config_path, storage_backend):
2370 | success_count += 1
2371 | elif client_type == 'claude_code':
2372 | # Claude Code uses project-level .mcp.json, handle separately
2373 | print_info(f" -> Claude Code: Configure via project .mcp.json (see instructions below)")
2374 | success_count += 1
2375 | except Exception as e:
2376 | print_warning(f" -> Failed to configure {client_type}: {e}")
2377 |
2378 | return success_count
2379 |
2380 | def configure_claude_desktop_multi_client(config_path, system_info, storage_backend="sqlite_vec"):
2381 | """Configure Claude Desktop for multi-client access."""
2382 | try:
2383 | import json
2384 |
2385 | # Read existing configuration
2386 | with open(config_path, 'r') as f:
2387 | config = json.load(f)
2388 |
2389 | # Ensure mcpServers section exists
2390 | if 'mcpServers' not in config:
2391 | config['mcpServers'] = {}
2392 |
2393 | # Update memory server configuration with multi-client settings
2394 | repo_path = str(Path.cwd()).replace('\\', '\\\\') # Escape backslashes for JSON
2395 | config['mcpServers']['memory'] = build_mcp_server_config(storage_backend, repo_path)
2396 |
2397 | # Write updated configuration
2398 | with open(config_path, 'w') as f:
2399 | json.dump(config, f, indent=2)
2400 |
2401 | print_info(f" [OK] Claude Desktop: Updated configuration for multi-client access")
2402 | return True
2403 |
2404 | except Exception as e:
2405 | print_warning(f" -> Claude Desktop configuration failed: {e}")
2406 | return False
2407 |
2408 | def configure_vscode_like_multi_client(config_path, client_type, storage_backend="sqlite_vec"):
2409 | """Configure VS Code or Cursor for multi-client access."""
2410 | try:
2411 | import json
2412 |
2413 | # For VS Code/Cursor, we provide instructions rather than modifying settings directly
2414 | # since MCP configuration varies by extension
2415 |
2416 | client_name = "VS Code" if client_type == 'vscode_mcp' else "Cursor"
2417 | print_info(f" -> {client_name}: MCP extension detected")
2418 | print_info(f" Add memory server to your MCP extension with these settings:")
2419 | print_info(f" - Backend: {storage_backend}")
2420 | if storage_backend == "sqlite_vec":
2421 | print_info(f" - Database: shared SQLite-vec database")
2422 | else:
2423 | print_info(f" - Database: shared ChromaDB database")
2424 | print_info(f" - See generic configuration below for details")
2425 | return True
2426 |
2427 | except Exception as e:
2428 | print_warning(f" -> {client_type} configuration failed: {e}")
2429 | return False
2430 |
2431 | def configure_continue_multi_client(config_path, storage_backend="sqlite_vec"):
2432 | """Configure Continue IDE for multi-client access."""
2433 | try:
2434 | import json
2435 |
2436 | # Read existing Continue configuration
2437 | with open(config_path, 'r') as f:
2438 | config = json.load(f)
2439 |
2440 | # Add or update MCP server configuration for Continue
2441 | if 'mcpServers' not in config:
2442 | config['mcpServers'] = {}
2443 |
2444 | config['mcpServers']['memory'] = build_mcp_server_config(storage_backend)
2445 |
2446 | # Write updated configuration
2447 | with open(config_path, 'w') as f:
2448 | json.dump(config, f, indent=2)
2449 |
2450 | print_info(f" [OK] Continue IDE: Updated configuration for multi-client access")
2451 | return True
2452 |
2453 | except Exception as e:
2454 | print_warning(f" -> Continue IDE configuration failed: {e}")
2455 | return False
2456 |
2457 | def configure_generic_mcp_multi_client(config_path, storage_backend="sqlite_vec"):
2458 | """Configure generic MCP client for multi-client access."""
2459 | try:
2460 | import json
2461 |
2462 | # Read existing configuration
2463 | with open(config_path, 'r') as f:
2464 | config = json.load(f)
2465 |
2466 | # Add memory server if not present
2467 | if 'mcpServers' not in config:
2468 | config['mcpServers'] = {}
2469 |
2470 | config['mcpServers']['memory'] = build_mcp_server_config(storage_backend)
2471 |
2472 | # Write updated configuration
2473 | with open(config_path, 'w') as f:
2474 | json.dump(config, f, indent=2)
2475 |
2476 | print_info(f" [OK] Generic MCP Client: Updated configuration")
2477 | return True
2478 |
2479 | except Exception as e:
2480 | print_warning(f" -> Generic MCP client configuration failed: {e}")
2481 | return False
2482 |
2483 | async def test_wal_mode_coordination():
2484 | """Test WAL mode storage coordination for multi-client access."""
2485 | try:
2486 | # Add src to path for import
2487 | sys.path.insert(0, str(Path(__file__).parent / "src"))
2488 |
2489 | from mcp_memory_service.storage.sqlite_vec import SqliteVecMemoryStorage
2490 | from mcp_memory_service.models.memory import Memory
2491 | from mcp_memory_service.utils.hashing import generate_content_hash
2492 |
2493 | import tempfile
2494 | import asyncio
2495 |
2496 | # Create a temporary database for testing
2497 | with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp:
2498 | test_db_path = tmp.name
2499 |
2500 | try:
2501 | # Test direct SQLite-vec storage with WAL mode
2502 | print_info(" -> Testing WAL mode coordination...")
2503 | storage = SqliteVecMemoryStorage(test_db_path)
2504 | await storage.initialize()
2505 |
2506 | # Test storing a memory
2507 | content = "Multi-client setup test - WAL mode verification"
2508 | test_memory = Memory(
2509 | content=content,
2510 | content_hash=generate_content_hash(content),
2511 | tags=["setup", "wal-test", "multi-client"],
2512 | memory_type="test"
2513 | )
2514 |
2515 | # Store memory
2516 | success, message = await storage.store(test_memory)
2517 | if not success:
2518 | print_warning(f" -> Memory storage failed: {message}")
2519 | return False
2520 |
2521 | # Test concurrent access simulation
2522 | storage2 = SqliteVecMemoryStorage(test_db_path)
2523 | await storage2.initialize()
2524 |
2525 | # Both should be able to read
2526 | results1 = await storage.search_by_tag(["setup"])
2527 | results2 = await storage2.search_by_tag(["setup"])
2528 |
2529 | if len(results1) != len(results2) or len(results1) == 0:
2530 | print_warning(" -> Concurrent read access test failed")
2531 | return False
2532 |
2533 | # Test concurrent write
2534 | content2 = "Second client test memory"
2535 | memory2 = Memory(
2536 | content=content2,
2537 | content_hash=generate_content_hash(content2),
2538 | tags=["setup", "client2"],
2539 | memory_type="test"
2540 | )
2541 |
2542 | success2, _ = await storage2.store(memory2)
2543 | if not success2:
2544 | print_warning(" -> Concurrent write access test failed")
2545 | return False
2546 |
2547 | # Verify both clients can see both memories
2548 | all_results = await storage.search_by_tag(["setup"])
2549 | if len(all_results) < 2:
2550 | print_warning(" -> Multi-client data sharing test failed")
2551 | return False
2552 |
2553 | storage.close()
2554 | storage2.close()
2555 |
2556 | print_info(" [OK] WAL mode coordination test passed")
2557 | return True
2558 |
2559 | finally:
2560 | # Cleanup test files
2561 | try:
2562 | os.unlink(test_db_path)
2563 | for ext in ["-wal", "-shm"]:
2564 | try:
2565 | os.unlink(test_db_path + ext)
2566 | except:
2567 | pass
2568 | except:
2569 | pass
2570 |
2571 | except Exception as e:
2572 | print_warning(f" -> WAL mode test failed: {e}")
2573 | return False
2574 |
2575 | def setup_shared_environment():
2576 | """Set up shared environment variables for multi-client access."""
2577 | try:
2578 | print_info(" -> Configuring shared environment variables...")
2579 |
2580 | # Set environment variables in current process (for testing)
2581 | os.environ['MCP_MEMORY_STORAGE_BACKEND'] = 'sqlite_vec'
2582 | os.environ['MCP_MEMORY_SQLITE_PRAGMAS'] = 'busy_timeout=15000,cache_size=20000'
2583 | os.environ['LOG_LEVEL'] = 'INFO'
2584 |
2585 | print_info(" [OK] Environment variables configured")
2586 |
2587 | # Provide instructions for permanent setup
2588 | system_info = detect_system()
2589 | if system_info["is_windows"]:
2590 | print_info(" -> For permanent setup, run these PowerShell commands as Administrator:")
2591 | print_info(" [System.Environment]::SetEnvironmentVariable('MCP_MEMORY_STORAGE_BACKEND', 'sqlite_vec', [System.EnvironmentVariableTarget]::User)")
2592 | print_info(" [System.Environment]::SetEnvironmentVariable('MCP_MEMORY_SQLITE_PRAGMAS', 'busy_timeout=15000,cache_size=20000', [System.EnvironmentVariableTarget]::User)")
2593 | print_info(" [System.Environment]::SetEnvironmentVariable('LOG_LEVEL', 'INFO', [System.EnvironmentVariableTarget]::User)")
2594 | else:
2595 | print_info(" -> For permanent setup, add to your shell profile:")
2596 | print_info(" export MCP_MEMORY_STORAGE_BACKEND=sqlite_vec")
2597 | print_info(" export MCP_MEMORY_SQLITE_PRAGMAS='busy_timeout=15000,cache_size=20000'")
2598 | print_info(" export LOG_LEVEL=INFO")
2599 |
2600 | return True
2601 |
2602 | except Exception as e:
2603 | print_warning(f" -> Environment setup failed: {e}")
2604 | return False
2605 |
2606 | def provide_generic_configuration(storage_backend="sqlite_vec"):
2607 | """Provide configuration instructions for any MCP client."""
2608 | print_info("")
2609 | print_info("Universal MCP Client Configuration:")
2610 | print_info("=" * 50)
2611 | print_info("For any MCP-compatible client, use these settings:")
2612 | print_info("")
2613 | print_info("MCP Server Configuration:")
2614 |
2615 | repo_path = str(Path.cwd())
2616 |
2617 | # Windows-style path
2618 | uv_cmd = UV_EXECUTABLE_PATH or "uv"
2619 | if platform.system() == 'Windows':
2620 | print_info(f" Command: {uv_cmd} --directory \"{repo_path}\" run memory")
2621 | print_info(f" Alternative: python -m mcp_memory_service.server")
2622 | else:
2623 | print_info(f" Command: {uv_cmd} --directory {repo_path} run memory")
2624 | print_info(f" Alternative: python -m mcp_memory_service.server")
2625 |
2626 | print_info("")
2627 | print_info("Environment Variables:")
2628 | print_info(f" MCP_MEMORY_STORAGE_BACKEND={storage_backend}")
2629 | if storage_backend == "sqlite_vec":
2630 | print_info(" MCP_MEMORY_SQLITE_PRAGMAS=busy_timeout=15000,cache_size=20000")
2631 | print_info(" LOG_LEVEL=INFO")
2632 |
2633 | print_info("")
2634 | print_info("Shared Database Location:")
2635 | if storage_backend == "sqlite_vec":
2636 | if platform.system() == 'Windows':
2637 | print_info(" %LOCALAPPDATA%\\mcp-memory\\sqlite_vec.db")
2638 | elif platform.system() == 'Darwin':
2639 | print_info(" ~/Library/Application Support/mcp-memory/sqlite_vec.db")
2640 | else:
2641 | print_info(" ~/.local/share/mcp-memory/sqlite_vec.db")
2642 | else: # chromadb
2643 | if platform.system() == 'Windows':
2644 | print_info(" %LOCALAPPDATA%\\mcp-memory\\chroma_db")
2645 | elif platform.system() == 'Darwin':
2646 | print_info(" ~/Library/Application Support/mcp-memory/chroma_db")
2647 | else:
2648 | print_info(" ~/.local/share/mcp-memory/chroma_db")
2649 |
2650 | print_info("")
2651 | print_info("Claude Code Project Configuration (.mcp.json):")
2652 | print_info(" {")
2653 | print_info(" \"mcpServers\": {")
2654 | print_info(" \"memory\": {")
2655 | print_info(f" \"command\": \"{UV_EXECUTABLE_PATH or 'uv'}\",")
2656 | print_info(f" \"args\": [\"--directory\", \"{repo_path}\", \"run\", \"memory\"],")
2657 | print_info(" \"env\": {")
2658 | print_info(f" \"MCP_MEMORY_STORAGE_BACKEND\": \"{storage_backend}\",")
2659 | if storage_backend == "sqlite_vec":
2660 | print_info(" \"MCP_MEMORY_SQLITE_PRAGMAS\": \"busy_timeout=15000,cache_size=20000\",")
2661 | print_info(" \"LOG_LEVEL\": \"INFO\"")
2662 | print_info(" }")
2663 | print_info(" }")
2664 | print_info(" }")
2665 | print_info(" }")
2666 |
2667 | def setup_universal_multi_client_access(system_info, args, storage_backend="sqlite_vec"):
2668 | """Configure multi-client access for any MCP-compatible clients."""
2669 | print_step("7", "Configuring Universal Multi-Client Access")
2670 |
2671 | print_info("Setting up multi-client coordination for all MCP applications...")
2672 | print_info("Benefits:")
2673 | print_info(" • Share memories between Claude Desktop, VS Code, Continue, and other MCP clients")
2674 | print_info(" • Seamless context sharing across development environments")
2675 | print_info(" • Single source of truth for all your project memories")
2676 | print_info("")
2677 |
2678 | # Test WAL mode coordination only for sqlite_vec
2679 | if storage_backend == "sqlite_vec":
2680 | try:
2681 | import asyncio
2682 | wal_success = asyncio.run(test_wal_mode_coordination())
2683 | if not wal_success:
2684 | print_error("WAL mode coordination test failed")
2685 | return False
2686 | except Exception as e:
2687 | print_error(f"Failed to test WAL mode coordination: {e}")
2688 | return False
2689 |
2690 | # Detect available MCP clients
2691 | detected_clients = detect_mcp_clients()
2692 | print_detected_clients(detected_clients)
2693 | print_info("")
2694 |
2695 | # Configure each detected client
2696 | print_info("Configuring detected clients...")
2697 | success_count = configure_detected_clients(detected_clients, system_info, storage_backend)
2698 |
2699 | # Set up shared environment variables
2700 | setup_shared_environment()
2701 |
2702 | # Provide generic configuration for manual setup
2703 | provide_generic_configuration(storage_backend)
2704 |
2705 | print_info("")
2706 | print_success(f"Multi-client setup complete! {success_count} clients configured automatically.")
2707 | print_info("")
2708 | print_info("Next Steps:")
2709 | print_info(" 1. Restart your applications (Claude Desktop, VS Code, etc.)")
2710 | print_info(" 2. All clients will share the same memory database")
2711 | print_info(" 3. Test: Store memory in one app, access from another")
2712 | print_info(" 4. For Claude Code: Create .mcp.json in your project directory")
2713 |
2714 | return True
2715 |
2716 | def _parse_arguments():
2717 | """Parse command-line arguments."""
2718 | parser = argparse.ArgumentParser(description="Install MCP Memory Service")
2719 | parser.add_argument('--dev', action='store_true', help='Install in development mode')
2720 | parser.add_argument('--chroma-path', type=str, help='Path to ChromaDB storage')
2721 | parser.add_argument('--backups-path', type=str, help='Path to backups storage')
2722 | parser.add_argument('--force-compatible-deps', action='store_true',
2723 | help='Force compatible versions of PyTorch (2.0.1) and sentence-transformers (2.2.2)')
2724 | parser.add_argument('--fallback-deps', action='store_true',
2725 | help='Use fallback versions of PyTorch (1.13.1) and sentence-transformers (2.2.2)')
2726 | parser.add_argument('--storage-backend', choices=['chromadb', 'sqlite_vec', 'auto_detect'],
2727 | help='Choose storage backend: chromadb (default), sqlite_vec (lightweight), or auto_detect (try chromadb, fallback to sqlite_vec)')
2728 | parser.add_argument('--skip-pytorch', action='store_true',
2729 | help='Skip PyTorch installation and use ONNX runtime with SQLite-vec backend instead')
2730 | parser.add_argument('--use-homebrew-pytorch', action='store_true',
2731 | help='Use existing Homebrew PyTorch installation instead of pip version')
2732 | parser.add_argument('--force-pytorch', action='store_true',
2733 | help='Force PyTorch installation even when using SQLite-vec (overrides auto-skip)')
2734 |
2735 | # New intelligent installer options
2736 | parser.add_argument('--legacy-hardware', action='store_true',
2737 | help='Optimize installation for legacy hardware (2013-2017 Intel Macs)')
2738 | parser.add_argument('--server-mode', action='store_true',
2739 | help='Install for server/headless deployment (minimal UI dependencies)')
2740 | parser.add_argument('--enable-http-api', action='store_true',
2741 | help='Enable HTTP/SSE API functionality')
2742 | parser.add_argument('--migrate-from-chromadb', action='store_true',
2743 | help='Migrate existing ChromaDB installation to selected backend')
2744 | parser.add_argument('--configure-claude-code', action='store_true',
2745 | help='Automatically configure Claude Code MCP integration with optimized settings')
2746 | parser.add_argument('--help-detailed', action='store_true',
2747 | help='Show detailed hardware-specific installation recommendations')
2748 | parser.add_argument('--generate-docs', action='store_true',
2749 | help='Generate personalized setup documentation for your hardware')
2750 | parser.add_argument('--setup-multi-client', action='store_true',
2751 | help='Configure multi-client access for any MCP-compatible applications (Claude, VS Code, Continue, etc.)')
2752 | parser.add_argument('--skip-multi-client-prompt', action='store_true',
2753 | help='Skip the interactive prompt for multi-client setup')
2754 | parser.add_argument('--install-claude-commands', action='store_true',
2755 | help='Install Claude Code commands for memory operations')
2756 | parser.add_argument('--skip-claude-commands-prompt', action='store_true',
2757 | help='Skip the interactive prompt for Claude Code commands')
2758 | parser.add_argument('--non-interactive', action='store_true',
2759 | help='Run in non-interactive mode using default values for all prompts')
2760 |
2761 | return parser.parse_args()
2762 |
2763 | def _handle_special_modes(args):
2764 | """Handle special help and documentation modes that exit early."""
2765 | if args.help_detailed:
2766 | show_detailed_help()
2767 | sys.exit(0)
2768 |
2769 | if args.generate_docs:
2770 | generate_personalized_docs()
2771 | sys.exit(0)
2772 |
2773 | def _detect_system_and_environment(args):
2774 | """Detect system configuration and return system info dict."""
2775 | print_header("MCP Memory Service Installation")
2776 |
2777 | print_step("1", "Detecting system")
2778 | system_info = detect_system()
2779 | gpu_info = detect_gpu()
2780 | memory_gb = detect_memory_gb()
2781 |
2782 | if memory_gb > 0:
2783 | print_info(f"System memory: {memory_gb:.1f}GB")
2784 |
2785 | return system_info, gpu_info, memory_gb
2786 |
2787 | def _recommend_backend(args, system_info, gpu_info, memory_gb):
2788 | """Recommend and set storage backend based on system configuration."""
2789 | if not args.storage_backend:
2790 | recommended_backend = recommend_backend_intelligent(system_info, gpu_info, memory_gb, args)
2791 | args.storage_backend = recommended_backend
2792 | print_info(f"Recommended backend: {recommended_backend}")
2793 |
2794 | def _configure_legacy_hardware(args, system_info):
2795 | """Configure installation for legacy hardware."""
2796 | if args.legacy_hardware or is_legacy_hardware(system_info):
2797 | print_step("1a", "Legacy Hardware Optimization")
2798 | args.storage_backend = "sqlite_vec"
2799 | args.use_homebrew_pytorch = True
2800 | print_success("Configuring for legacy hardware compatibility")
2801 | print_info("• SQLite-vec backend selected")
2802 | print_info("• Homebrew PyTorch integration enabled")
2803 | print_info("• ONNX runtime will be configured")
2804 |
2805 | def _configure_server_mode(args):
2806 | """Configure installation for server mode."""
2807 | if args.server_mode:
2808 | print_step("1b", "Server Mode Configuration")
2809 | args.storage_backend = "sqlite_vec"
2810 | print_success("Configuring for server deployment")
2811 | print_info("• Lightweight SQLite-vec backend")
2812 | print_info("• Minimal UI dependencies")
2813 |
2814 | def _configure_http_api(args):
2815 | """Configure HTTP/SSE API settings."""
2816 | if args.enable_http_api:
2817 | print_step("1c", "HTTP/SSE API Configuration")
2818 | if args.storage_backend == "chromadb":
2819 | print_warning("HTTP/SSE API works best with SQLite-vec backend")
2820 | if args.non_interactive:
2821 | print_info("Non-interactive mode: switching to SQLite-vec for HTTP API compatibility")
2822 | args.storage_backend = "sqlite_vec"
2823 | else:
2824 | response = prompt_user_input("Switch to SQLite-vec for optimal HTTP API experience? (y/N, press Enter for N): ", "")
2825 | if response.lower().startswith('y'):
2826 | args.storage_backend = "sqlite_vec"
2827 |
2828 | def _setup_chromadb_migration(args):
2829 | """Set up ChromaDB migration if requested."""
2830 | if not args.migrate_from_chromadb:
2831 | return
2832 |
2833 | print_step("1d", "Migration Setup")
2834 | print_info("Preparing to migrate from existing ChromaDB installation")
2835 |
2836 | chromadb_paths = [
2837 | os.path.expanduser("~/.mcp_memory_chroma"),
2838 | os.path.expanduser("~/Library/Application Support/mcp-memory/chroma_db"),
2839 | os.path.expanduser("~/.local/share/mcp-memory/chroma_db")
2840 | ]
2841 |
2842 | chromadb_found = None
2843 | for path in chromadb_paths:
2844 | if os.path.exists(path):
2845 | chromadb_found = path
2846 | break
2847 |
2848 | if chromadb_found:
2849 | print_success(f"Found ChromaDB data at: {chromadb_found}")
2850 | args.storage_backend = "sqlite_vec"
2851 | args.chromadb_found = chromadb_found
2852 | print_info("Migration will run after installation completes")
2853 | else:
2854 | print_warning("No ChromaDB data found at standard locations")
2855 | if args.non_interactive:
2856 | print_info("Non-interactive mode: skipping ChromaDB migration")
2857 | args.migrate_from_chromadb = False
2858 | else:
2859 | manual_path = prompt_user_input("Enter ChromaDB path manually (or press Enter to skip): ", "")
2860 | if manual_path and os.path.exists(manual_path):
2861 | args.storage_backend = "sqlite_vec"
2862 | args.chromadb_found = manual_path
2863 | else:
2864 | print_info("Skipping migration - no valid ChromaDB path provided")
2865 | args.migrate_from_chromadb = False
2866 |
2867 | def _install_compatible_dependencies(args, system_info):
2868 | """Install compatible PyTorch/transformers versions for macOS Intel."""
2869 | if not args.force_compatible_deps:
2870 | return
2871 |
2872 | if not (system_info["is_macos"] and system_info["is_x86"]):
2873 | print_warning("--force-compatible-deps is only applicable for macOS with Intel CPUs")
2874 | return
2875 |
2876 | print_info("Installing compatible dependencies as requested...")
2877 | python_version = sys.version_info
2878 |
2879 | if python_version >= (3, 13):
2880 | torch_version, torch_vision_version = "2.3.0", "0.18.0"
2881 | torch_audio_version, st_version = "2.3.0", "3.0.0"
2882 | else:
2883 | torch_version, torch_vision_version = "2.0.1", "2.0.1"
2884 | torch_audio_version, st_version = "2.0.1", "2.2.2"
2885 |
2886 | try:
2887 | subprocess.check_call([
2888 | sys.executable, '-m', 'pip', 'install',
2889 | f"torch=={torch_version}", f"torchvision=={torch_vision_version}",
2890 | f"torchaudio=={torch_audio_version}", f"sentence-transformers=={st_version}"
2891 | ])
2892 | print_success("Compatible dependencies installed successfully")
2893 | except subprocess.SubprocessError as e:
2894 | print_error(f"Failed to install compatible dependencies: {e}")
2895 |
2896 | def _install_fallback_dependencies(args):
2897 | """Install fallback PyTorch/transformers versions for troubleshooting."""
2898 | if not args.fallback_deps:
2899 | return
2900 |
2901 | print_info("Installing fallback dependencies as requested...")
2902 | python_version = sys.version_info
2903 |
2904 | if python_version >= (3, 13):
2905 | torch_version, torch_vision_version = "2.3.0", "0.18.0"
2906 | torch_audio_version, st_version = "2.3.0", "3.0.0"
2907 | else:
2908 | torch_version, torch_vision_version = "1.13.1", "0.14.1"
2909 | torch_audio_version, st_version = "0.13.1", "2.2.2"
2910 |
2911 | try:
2912 | subprocess.check_call([
2913 | sys.executable, '-m', 'pip', 'install',
2914 | f"torch=={torch_version}", f"torchvision=={torch_vision_version}",
2915 | f"torchaudio=={torch_audio_version}", f"sentence-transformers=={st_version}"
2916 | ])
2917 | print_success("Fallback dependencies installed successfully")
2918 | except subprocess.SubprocessError as e:
2919 | print_error(f"Failed to install fallback dependencies: {e}")
2920 |
2921 | def _optimize_pytorch_for_backend(args):
2922 | """Auto-skip PyTorch for sqlite_vec backend."""
2923 | if (args.storage_backend == "sqlite_vec" and
2924 | not args.skip_pytorch and
2925 | not args.force_pytorch):
2926 | print_step("1d", "Optimizing for SQLite-vec setup")
2927 | args.skip_pytorch = True
2928 | print_success("Auto-skipping PyTorch installation for SQLite-vec backend")
2929 | print_info("• SQLite-vec uses SQLite for vector storage (lighter than ChromaDB)")
2930 | print_info("• Note: Embedding models still require PyTorch/SentenceTransformers")
2931 | print_info("• Add --force-pytorch if you want PyTorch installed here")
2932 | print_warning("• You'll need PyTorch available for embedding functionality")
2933 |
2934 | def _setup_logging_and_detect_system(args):
2935 | """Initialize logging and detect system configuration."""
2936 | try:
2937 | log_file_path = setup_installer_logging()
2938 | except Exception as e:
2939 | print(f"Warning: Could not set up logging: {e}")
2940 | log_file_path = None
2941 |
2942 | system_info, gpu_info, memory_gb = _detect_system_and_environment(args)
2943 | _recommend_backend(args, system_info, gpu_info, memory_gb)
2944 | _configure_legacy_hardware(args, system_info)
2945 | _configure_server_mode(args)
2946 | _configure_http_api(args)
2947 | _setup_chromadb_migration(args)
2948 | _install_compatible_dependencies(args, system_info)
2949 | _install_fallback_dependencies(args)
2950 | _optimize_pytorch_for_backend(args)
2951 |
2952 | return log_file_path, system_info
2953 |
2954 | def _execute_core_installation(args, system_info):
2955 | """Execute the core installation steps (dependencies, package, paths, verification)."""
2956 | if not check_dependencies():
2957 | sys.exit(1)
2958 |
2959 | if not install_package(args):
2960 | if system_info["is_macos"] and system_info["is_x86"]:
2961 | print_warning("Installation failed on macOS Intel.")
2962 | print_info("Try running the script with '--force-compatible-deps' to force compatible versions:")
2963 | print_info("python install.py --force-compatible-deps")
2964 | sys.exit(1)
2965 |
2966 | if not configure_paths(args):
2967 | print_warning("Path configuration failed, but installation may still work")
2968 |
2969 | _verify_installation_with_suggestions(system_info)
2970 |
2971 | def _verify_installation_with_suggestions(system_info):
2972 | """Verify installation and provide platform-specific troubleshooting suggestions."""
2973 | if not verify_installation():
2974 | print_warning("Installation verification failed, but installation may still work")
2975 | if system_info["is_macos"] and system_info["is_x86"]:
2976 | python_version = sys.version_info
2977 | print_info("For macOS Intel compatibility issues, try these steps:")
2978 | print_info("1. First uninstall current packages: pip uninstall -y torch torchvision torchaudio sentence-transformers")
2979 | print_info("2. Then reinstall with compatible versions: python install.py --force-compatible-deps")
2980 |
2981 | if python_version >= (3, 13):
2982 | print_info("For Python 3.13+, you may need to manually install the following:")
2983 | print_info("pip install torch==2.3.0 torchvision==0.18.0 torchaudio==2.3.0")
2984 | print_info("pip install sentence-transformers==3.0.0")
2985 |
2986 | def _execute_chromadb_migration(args):
2987 | """Execute ChromaDB migration if requested."""
2988 | if not (args.migrate_from_chromadb and hasattr(args, 'chromadb_found') and args.chromadb_found):
2989 | return
2990 |
2991 | print_step("6", "Migrating from ChromaDB")
2992 | try:
2993 | migration_script = "scripts/migrate_chroma_to_sqlite.py"
2994 | if os.path.exists(migration_script):
2995 | print_info("Running migration script...")
2996 | subprocess.check_call([sys.executable, migration_script, "--auto-confirm"])
2997 | print_success("Migration completed successfully!")
2998 | else:
2999 | print_warning("Migration script not found - manual migration required")
3000 | print_info("Run: python scripts/migrate_chroma_to_sqlite.py")
3001 | except subprocess.SubprocessError as e:
3002 | print_error(f"Migration failed: {e}")
3003 | print_info("You can run migration manually later with:")
3004 | print_info("python scripts/migrate_chroma_to_sqlite.py")
3005 |
3006 | def _configure_claude_code_if_requested(args, system_info):
3007 | """Configure Claude Code integration if requested."""
3008 | if args.configure_claude_code:
3009 | if not configure_claude_code_integration(system_info):
3010 | print_warning("Claude Code integration configuration failed")
3011 | print_info("You can configure it manually later using the documentation")
3012 |
3013 | def _handle_claude_code_commands(args):
3014 | """Handle Claude Code commands installation."""
3015 | should_install_commands = args.install_claude_commands
3016 |
3017 | if not should_install_commands and not args.skip_claude_commands_prompt:
3018 | if install_claude_commands is not None and check_claude_code_cli is not None:
3019 | claude_available, _ = check_claude_code_cli()
3020 | if claude_available:
3021 | should_install_commands = _prompt_for_claude_commands(args)
3022 |
3023 | if should_install_commands and install_claude_commands is not None:
3024 | _install_claude_commands_internal()
3025 |
3026 | def _prompt_for_claude_commands(args):
3027 | """Prompt user for Claude Code commands installation."""
3028 | print_step("7", "Optional Claude Code Commands")
3029 | print_info("Claude Code CLI detected! You can install memory operation commands.")
3030 | print_info("Commands would include: /memory-store, /memory-recall, /memory-search, /memory-health")
3031 |
3032 | if args.non_interactive:
3033 | print_info("Non-interactive mode: skipping Claude Code commands installation")
3034 | return False
3035 |
3036 | print("\n" + "=" * 60)
3037 | print("⚠️ USER INPUT REQUIRED")
3038 | print("=" * 60)
3039 | response = input("Install Claude Code memory commands? (y/N, press Enter for N): ")
3040 | print("=" * 60 + "\n")
3041 | return response.lower().startswith('y')
3042 |
3043 | def _install_claude_commands_internal():
3044 | """Install Claude Code commands."""
3045 | print_step("7", "Installing Claude Code Commands")
3046 | try:
3047 | if install_claude_commands(verbose=True):
3048 | print_success("Claude Code commands installed successfully!")
3049 | else:
3050 | print_warning("Claude Code commands installation had issues")
3051 | print_info("You can install them manually later with:")
3052 | print_info("python scripts/claude_commands_utils.py")
3053 | except Exception as e:
3054 | print_error(f"Failed to install Claude Code commands: {str(e)}")
3055 | print_info("You can install them manually later with:")
3056 | print_info("python scripts/claude_commands_utils.py")
3057 |
3058 | def _print_final_setup_notices():
3059 | """Print first-time setup expectations."""
3060 | print_header("Installation Complete")
3061 | print_info("")
3062 | print_info("⚠️ FIRST-TIME SETUP EXPECTATIONS:")
3063 | print_info("On first run, you may see these NORMAL warnings:")
3064 | print_info(" • 'No snapshots directory' - Model will download automatically (~25MB)")
3065 | print_info(" • 'TRANSFORMERS_CACHE deprecated' - Informational, doesn't affect operation")
3066 | print_info(" • Model download progress - One-time download (1-2 minutes)")
3067 | print_info("")
3068 | print_info("These warnings disappear after the first successful run.")
3069 | print_info("See docs/first-time-setup.md for details.")
3070 | print_info("")
3071 |
3072 | def _determine_final_backend(system_info):
3073 | """Determine final storage backend based on system configuration."""
3074 | if system_info["is_macos"] and system_info["is_x86"] and system_info.get("has_homebrew_pytorch"):
3075 | os.environ['MCP_MEMORY_STORAGE_BACKEND'] = 'sqlite_vec'
3076 | return 'sqlite_vec'
3077 | return os.environ.get('MCP_MEMORY_STORAGE_BACKEND', 'chromadb')
3078 |
3079 | def _setup_multi_client_access(args, system_info, final_backend):
3080 | """Set up multi-client access if requested or offered."""
3081 | if args.setup_multi_client:
3082 | _execute_explicit_multi_client_setup(system_info, args, final_backend)
3083 | elif should_offer_multi_client_setup(args, final_backend):
3084 | _handle_interactive_multi_client_setup(args, system_info, final_backend)
3085 |
3086 | def _execute_explicit_multi_client_setup(system_info, args, final_backend):
3087 | """Execute multi-client setup when explicitly requested."""
3088 | try:
3089 | setup_universal_multi_client_access(system_info, args, final_backend)
3090 | except Exception as e:
3091 | print_error(f"Multi-client setup failed: {e}")
3092 | print_info("You can set up multi-client access manually using:")
3093 | print_info("python setup_multi_client_complete.py")
3094 |
3095 | def _handle_interactive_multi_client_setup(args, system_info, final_backend):
3096 | """Handle interactive multi-client setup prompt."""
3097 | print_info("")
3098 | print_info("Multi-Client Access Available!")
3099 | print_info("")
3100 | print_info("The MCP Memory Service can be configured for multi-client access, allowing")
3101 | print_info("multiple applications and IDEs to share the same memory database.")
3102 | print_info("")
3103 | print_info("Benefits:")
3104 | print_info(" • Share memories between Claude Desktop, VS Code, Continue, and other MCP clients")
3105 | print_info(" • Seamless context sharing across development environments")
3106 | print_info(" • Single source of truth for all your project memories")
3107 | print_info("")
3108 |
3109 | try:
3110 | if args.non_interactive:
3111 | print_info("Non-interactive mode: skipping multi-client configuration")
3112 | response = 'n'
3113 | else:
3114 | print("\n" + "=" * 60)
3115 | print("⚠️ USER INPUT REQUIRED")
3116 | print("=" * 60)
3117 | response = input("Would you like to configure multi-client access? (y/N, press Enter for N): ").strip().lower()
3118 | print("=" * 60 + "\n")
3119 |
3120 | if response in ['y', 'yes']:
3121 | print_info("")
3122 | _execute_explicit_multi_client_setup(system_info, args, final_backend)
3123 | else:
3124 | print_info("Skipping multi-client setup. You can configure it later using:")
3125 | print_info("python setup_multi_client_complete.py")
3126 | except (EOFError, KeyboardInterrupt):
3127 | print_info("\nSkipping multi-client setup. You can configure it later using:")
3128 | print_info("python setup_multi_client_complete.py")
3129 |
3130 | print_info("")
3131 |
3132 | def _print_backend_configuration(final_backend, system_info):
3133 | """Print final backend configuration and recommendations."""
3134 | use_onnx = os.environ.get('MCP_MEMORY_USE_ONNX', '').lower() in ('1', 'true', 'yes')
3135 |
3136 | print_info("You can now run the MCP Memory Service using the 'memory' command")
3137 | print_info(f"Storage Backend: {final_backend.upper()}")
3138 |
3139 | if final_backend == 'sqlite_vec':
3140 | print_success("Using SQLite-vec - lightweight, fast, minimal dependencies")
3141 | print_info(" • No complex dependencies or build issues")
3142 | print_info(" • Excellent performance for typical use cases")
3143 | else:
3144 | print_success("Using ChromaDB - full-featured vector database")
3145 | print_info(" • Advanced features and extensive ecosystem")
3146 |
3147 | if use_onnx:
3148 | print_info("[OK] Using ONNX Runtime for inference")
3149 | print_info(" • Compatible with Homebrew PyTorch")
3150 | print_info(" • Reduced dependencies for better compatibility")
3151 |
3152 | print_info("For more information, see:")
3153 | print_info(" • Installation Guide: docs/guides/INSTALLATION_MASTER.md")
3154 | print_info(" • Backend Comparison: docs/guides/STORAGE_BACKENDS.md")
3155 | if system_info["is_macos"] and system_info["is_x86"] and is_legacy_hardware(system_info):
3156 | print_info(" • Legacy Mac Guide: docs/platforms/macos-intel-legacy.md")
3157 | print_info(" • Main README: README.md")
3158 |
3159 | def _print_macos_intel_notes(system_info):
3160 | """Print macOS Intel-specific notes and troubleshooting tips."""
3161 | if not (system_info["is_macos"] and system_info["is_x86"]):
3162 | return
3163 |
3164 | print_info("\nMacOS Intel Notes:")
3165 |
3166 | if system_info.get("has_homebrew_pytorch"):
3167 | print_info("- Using Homebrew PyTorch installation: " + system_info.get("homebrew_pytorch_version", "Unknown"))
3168 | print_info("- The MCP Memory Service is configured to use SQLite-vec + ONNX runtime")
3169 | print_info("- To start the memory service, use:")
3170 | print_info(" export MCP_MEMORY_USE_ONNX=1")
3171 | print_info(" export MCP_MEMORY_STORAGE_BACKEND=sqlite_vec")
3172 | print_info(" memory")
3173 | else:
3174 | print_info("- If you encounter issues, try the --force-compatible-deps option")
3175 | python_version = sys.version_info
3176 | if python_version >= (3, 13):
3177 | print_info("- For optimal performance on Intel Macs with Python 3.13+, torch==2.3.0 and sentence-transformers==3.0.0 are recommended")
3178 | print_info("- You can manually install these versions with:")
3179 | print_info(" pip install torch==2.3.0 torchvision==0.18.0 torchaudio==2.3.0 sentence-transformers==3.0.0")
3180 | else:
3181 | print_info("- For optimal performance on Intel Macs, torch==2.0.1 and sentence-transformers==2.2.2 are recommended")
3182 | print_info("- You can manually install these versions with:")
3183 | print_info(" pip install torch==2.0.1 torchvision==2.0.1 torchaudio==2.0.1 sentence-transformers==2.2.2")
3184 |
3185 | print_info("\nTroubleshooting Tips:")
3186 | print_info("- If you have a Homebrew PyTorch installation, use: --use-homebrew-pytorch")
3187 | print_info("- To completely skip PyTorch installation, use: --skip-pytorch")
3188 | print_info("- To force the SQLite-vec backend, use: --storage-backend sqlite_vec")
3189 | print_info("- For a quick test, try running: python test_memory.py")
3190 |
3191 | def _cleanup_and_exit(log_file_path):
3192 | """Clean up logging system and exit."""
3193 | try:
3194 | cleanup_installer_logging()
3195 | if log_file_path:
3196 | print(f"\nInstallation log saved to: {log_file_path}")
3197 | except Exception:
3198 | pass # Silently ignore cleanup errors
3199 |
3200 | def main():
3201 | """Main installation function."""
3202 | args = _parse_arguments()
3203 | _handle_special_modes(args)
3204 |
3205 | log_file_path, system_info = _setup_logging_and_detect_system(args)
3206 | _execute_core_installation(args, system_info)
3207 | _execute_chromadb_migration(args)
3208 | _configure_claude_code_if_requested(args, system_info)
3209 | _handle_claude_code_commands(args)
3210 |
3211 | _print_final_setup_notices()
3212 | final_backend = _determine_final_backend(system_info)
3213 | _setup_multi_client_access(args, system_info, final_backend)
3214 | _print_backend_configuration(final_backend, system_info)
3215 | _print_macos_intel_notes(system_info)
3216 | _cleanup_and_exit(log_file_path)
3217 |
3218 | if __name__ == "__main__":
3219 | try:
3220 | main()
3221 | except KeyboardInterrupt:
3222 | print("\nInstallation interrupted by user")
3223 | cleanup_installer_logging()
3224 | sys.exit(1)
3225 | except Exception as e:
3226 | print(f"\nInstallation failed with error: {e}")
3227 | cleanup_installer_logging()
3228 | sys.exit(1)
```