This is page 15 of 35. Use http://codebase.md/doobidoo/mcp-memory-service?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
--------------------------------------------------------------------------------
/claude-hooks/README-phase2.md:
--------------------------------------------------------------------------------
```markdown
# Claude Code Memory Awareness - Phase 2: Intelligent Context Updates
## Overview
Phase 2 transforms Claude Code from a memory-aware system into a **conversation-intelligent** development assistant. While Phase 1 provided automatic memory injection at session start, Phase 2 adds **real-time conversation analysis** and **dynamic context updates** during active coding sessions.
## 🎯 Phase 2 Features
### 1. **Dynamic Memory Loading**
- **Real-time Topic Detection**: Analyzes conversation flow to detect topic shifts
- **Automatic Context Updates**: Injects relevant memories as conversation evolves
- **Deduplication**: Avoids re-injecting already loaded memories
- **Rate Limiting**: Prevents context overload with intelligent cooldown periods
### 2. **Conversation Intelligence**
- **Natural Language Processing**: Extracts topics, entities, and intent from conversations
- **Semantic Analysis**: Matches conversation content with stored memories
- **Code Context Detection**: Understands code blocks, file paths, and technical discussions
- **Intent Classification**: Recognizes debugging, implementation, planning, and optimization activities
### 3. **Cross-Session Intelligence**
- **Session Tracking**: Links related conversations across different sessions
- **Conversation Threading**: Builds conversation threads over time
- **Progress Continuity**: Connects outcomes from previous sessions to current work
- **Recurring Pattern Detection**: Identifies recurring topics and workflow patterns
### 4. **Enhanced Memory Scoring**
- **Multi-Factor Algorithm**: Combines time decay, tag relevance, content matching, and conversation context
- **Dynamic Weight Adjustment**: Adjusts scoring weights based on conversation analysis
- **Context Awareness**: Prioritizes memories matching current conversation topics
- **User Behavior Learning**: Adapts to individual developer patterns over time
## 🏗️ Technical Architecture
### Core Components
#### 1. **Conversation Analyzer** (`utilities/conversation-analyzer.js`)
```javascript
const analysis = analyzeConversation(conversationText, {
extractTopics: true,
extractEntities: true,
detectIntent: true,
detectCodeContext: true,
minTopicConfidence: 0.3
});
// Results: topics, entities, intent, codeContext, confidence
```
**Capabilities:**
- **Topic Detection**: 15+ technical topic categories (database, debugging, architecture, etc.)
- **Entity Extraction**: Technologies, frameworks, languages, tools
- **Intent Recognition**: Learning, problem-solving, development, optimization, review, planning
- **Code Context**: Detects code blocks, file paths, error messages, commands
#### 2. **Topic Change Detection** (`core/topic-change.js`)
```javascript
const changes = detectTopicChanges(previousAnalysis, currentAnalysis);
if (changes.hasTopicShift && changes.significanceScore > threshold) {
await triggerDynamicMemoryLoading();
}
```
**Features:**
- **Significance Scoring**: Calculates importance of topic changes
- **New Topic Detection**: Identifies emerging conversation topics
- **Intent Change Tracking**: Monitors shifts in conversation purpose
- **Threshold Management**: Prevents noise from minor changes
#### 3. **Enhanced Memory Scorer** (`utilities/memory-scorer.js`)
```javascript
const scoredMemories = scoreMemoryRelevance(memories, projectContext, {
includeConversationContext: true,
conversationAnalysis: analysis,
weights: {
timeDecay: 0.25,
tagRelevance: 0.35,
contentRelevance: 0.15,
conversationRelevance: 0.25
}
});
```
**Algorithm:**
- **Time Decay (25%)**: Recent memories weighted higher
- **Tag Relevance (35%)**: Project and technology tag matching
- **Content Relevance (15%)**: Keyword and semantic matching
- **Conversation Relevance (25%)**: Current topic and intent alignment
#### 4. **Session Tracker** (`utilities/session-tracker.js`)
```javascript
const sessionTracker = getSessionTracker();
await sessionTracker.startSession(sessionId, context);
const continuityContext = await sessionTracker.getConversationContext(
projectContext,
{ maxPreviousSessions: 3, maxDaysBack: 7 }
);
```
**Intelligence Features:**
- **Session Linking**: Connects related sessions across time
- **Conversation Threading**: Builds multi-session conversation threads
- **Progress Tracking**: Monitors outcomes and task completion
- **Pattern Recognition**: Identifies recurring topics and workflows
#### 5. **Dynamic Context Updater** (`utilities/dynamic-context-updater.js`)
```javascript
const updater = new DynamicContextUpdater({
updateThreshold: 0.3,
maxMemoriesPerUpdate: 3,
updateCooldownMs: 30000,
enableCrossSessionContext: true
});
await updater.processConversationUpdate(
conversationText,
memoryServiceConfig,
contextInjector
);
```
**Orchestration:**
- **Update Triggering**: Determines when to inject new context
- **Memory Querying**: Fetches relevant memories from service
- **Context Formatting**: Creates beautiful markdown context updates
- **Rate Management**: Prevents context overload with smart limiting
## 🔧 Configuration
### Phase 2 Configuration Options
```json
{
"conversationAnalysis": {
"enableTopicDetection": true,
"enableEntityExtraction": true,
"enableIntentDetection": true,
"enableCodeContextDetection": true,
"minTopicConfidence": 0.3,
"maxTopicsPerAnalysis": 10,
"analysisDebounceMs": 2000
},
"dynamicContextUpdate": {
"enabled": true,
"updateThreshold": 0.3,
"maxMemoriesPerUpdate": 3,
"updateCooldownMs": 30000,
"maxUpdatesPerSession": 10,
"debounceMs": 5000,
"enableCrossSessionContext": true
},
"sessionTracking": {
"enabled": true,
"maxSessionHistory": 50,
"maxConversationDepth": 10,
"sessionExpiryDays": 30,
"enableConversationThreads": true,
"enableProgressTracking": true
},
"memoryScoring": {
"weights": {
"timeDecay": 0.25,
"tagRelevance": 0.35,
"contentRelevance": 0.15,
"conversationRelevance": 0.25
},
"enableConversationContext": true
}
}
```
## 🚀 How Phase 2 Works
### 1. **Session Initialization**
```javascript
// Session starts with Phase 1 memory injection
await sessionStart.onSessionStart(context);
// Phase 2 initializes dynamic tracking
await dynamicUpdater.initialize(sessionContext);
await sessionTracker.startSession(sessionId, context);
```
### 2. **Real-time Conversation Monitoring**
```javascript
// As conversation evolves, analyze changes
const currentAnalysis = analyzeConversation(conversationText);
const changes = detectTopicChanges(previousAnalysis, currentAnalysis);
// Trigger dynamic updates for significant changes
if (changes.significanceScore > threshold) {
await triggerContextUpdate();
}
```
### 3. **Dynamic Context Injection**
```markdown
🧠 **Dynamic Context Update**
**New topics detected**: database, performance
**Recent session context**:
• Implementation completed 2 hours ago
• Debugging session completed yesterday
**Relevant context**:
🔥 Database optimization techniques for SQLite - Fixed query performance issues...
*database, optimization, sqlite*
⭐ Performance profiling guide - How to identify bottlenecks...
*performance, debugging, profiling*
---
```
### 4. **Cross-Session Intelligence**
```javascript
// Link current session to previous related work
const continuityContext = await sessionTracker.getConversationContext(projectContext);
// Include insights from previous sessions
if (continuityContext.recentSessions.length > 0) {
updateMessage += formatCrossSessionContext(continuityContext);
}
```
## 📊 Example Workflow
### Scenario: Database Performance Issue
1. **Initial Session Context** (Phase 1)
```markdown
🧠 Relevant Memory Context
## Recent Insights
- Authentication system completed yesterday
- New user registration implemented
## Project Context: ecommerce-app
- Language: Python, JavaScript
- Framework: Django, React
```
2. **User Starts Discussion**
```
User: "I'm noticing the user queries are really slow,
taking 2-3 seconds to load the dashboard"
```
3. **Dynamic Analysis Triggers**
- **Topics Detected**: `performance`, `database`, `optimization`
- **Intent**: `problem-solving`
- **Significance Score**: `0.7` (high)
4. **Dynamic Context Update** (Phase 2)
```markdown
🧠 **Dynamic Context Update**
**New topics detected**: performance, database
**Focus shifted to**: problem-solving
**Relevant context**:
🔥 Database indexing strategy for user queries - Added composite indexes...
*database, performance, indexing*
⭐ Query optimization patterns in Django - Use select_related() and prefetch...
*django, optimization, queries*
---
```
5. **Continued Evolution**
- As conversation progresses through debugging → solution → testing
- Each topic shift triggers relevant memory injection
- Previous context remains available, new context adds incrementally
## 🧪 Testing Phase 2
### Run Integration Tests
```bash
cd claude-hooks
node tests/phase2-integration-test.js
```
**Test Coverage:**
- Conversation Analysis (topic/entity/intent detection)
- Topic Change Detection (significance scoring)
- Enhanced Memory Scoring (conversation context)
- Session Tracking (cross-session intelligence)
- Dynamic Context Updates (rate limiting, formatting)
- Full Integration (conversation flow simulation)
### Manual Testing
```bash
# Test conversation analyzer
node -e "
const { analyzeConversation } = require('./utilities/conversation-analyzer');
const result = analyzeConversation('Debug database performance issues');
console.log('Topics:', result.topics.map(t => t.name));
"
# Test dynamic updates
node -e "
const { DynamicContextUpdater } = require('./utilities/dynamic-context-updater');
const updater = new DynamicContextUpdater();
console.log('Stats:', updater.getStats());
"
```
## 🎯 Benefits of Phase 2
### For Developers
- **Zero Cognitive Load**: Context updates happen automatically during conversations
- **Perfect Timing**: Memories appear exactly when relevant to current discussion
- **Conversation Intelligence**: AI understands context, intent, and technical discussions
- **Progressive Learning**: Each conversation builds upon previous knowledge
### For Development Workflow
- **Seamless Integration**: Works transparently during normal coding sessions
- **Cross-Session Continuity**: Never lose track of progress across different sessions
- **Intelligent Prioritization**: Most relevant memories surface first
- **Pattern Recognition**: Recurring issues and solutions automatically identified
### Technical Performance
- **Efficient Processing**: Smart rate limiting and debouncing prevent overload
- **Minimal Latency**: <500ms response time for topic detection and memory queries
- **Scalable Architecture**: Handles 100+ active memories per session
- **Resource Optimization**: Intelligent deduplication and context management
## 🔮 Phase 2 vs Phase 1 Comparison
| Feature | Phase 1 | Phase 2 |
|---------|---------|---------|
| **Memory Injection** | Session start only | Real-time during conversation |
| **Context Awareness** | Project-based | Project + conversation topics |
| **Intelligence** | Static scoring | Dynamic conversation analysis |
| **Session Linking** | None | Cross-session intelligence |
| **Update Frequency** | Once per session | Multiple times as topics evolve |
| **Memory Scoring** | 4-factor algorithm | 5-factor with conversation context |
| **User Experience** | Good contextual start | Intelligent conversation partner |
## 🚀 What's Next: Phase 3
Phase 2 completes the **Intelligent Context Updates** milestone. The next phase will focus on:
- **Advanced Memory Consolidation**: AI-powered memory organization and summarization
- **Team Knowledge Sharing**: Multi-developer memory contexts and collaboration
- **Predictive Context Loading**: Anticipate needed memories before topics emerge
- **Custom Memory Types**: Specialized memory categories for different development activities
- **Integration APIs**: Third-party tool integration and memory syndication
**Phase 2 represents a major leap forward in AI-assisted development - from memory-aware to conversation-intelligent coding assistance.** 🧠✨
```
--------------------------------------------------------------------------------
/archive/docs-removed-2025-08-23/sessions/MCP_ENHANCEMENT_SESSION_MEMORY_v4.1.0.md:
--------------------------------------------------------------------------------
```markdown
# MCP Memory Service Enhancement Session - v4.1.0 Complete MCP Specification Compliance
**Session Date**: August 6, 2025
**Version**: 4.1.0
**Status**: ✅ **COMPLETE - Full MCP Specification Compliance Achieved**
## Executive Summary
This enhancement session successfully implemented **complete MCP (Model Context Protocol) specification compliance** for the MCP Memory Service, transforming it from a basic memory storage system into a fully-featured, specification-compliant MCP server with advanced capabilities.
## Major Achievements
### 🎯 Full MCP Specification Compliance
- **Enhanced Resources System**: URI-based access to memory collections with 6 new resource endpoints
- **Guided Prompts Framework**: 5 interactive workflows for memory operations with proper schemas
- **Progress Tracking System**: Real-time notifications for long operations with MCP-compliant protocol
- **Complete Protocol Coverage**: All MCP resource, prompt, and notification features implemented
### 📊 Key Implementation Statistics
- **New Resource Endpoints**: 6 (`memory://stats`, `memory://tags`, `memory://recent/{n}`, `memory://tag/{tagname}`, `memory://search/{query}`)
- **New Guided Prompts**: 5 (`memory_review`, `memory_analysis`, `knowledge_export`, `memory_cleanup`, `learning_session`)
- **Progress Tracking**: 2 enhanced operations (`delete_by_tags`, `dashboard_optimize_db`) with real-time updates
- **Code Coverage**: ~500 new lines of implementation code across multiple modules
## Technical Implementation Details
### 1. Enhanced Resources System Implementation
#### New Resource Endpoints
```python
# URI-based resource access patterns implemented:
memory://stats # Real-time database statistics
memory://tags # Complete list of available tags
memory://recent/{n} # N most recent memories
memory://tag/{tagname} # Memories filtered by specific tag
memory://search/{query} # Dynamic search with structured results
```
#### Key Features
- **JSON Response Format**: All resources return structured JSON data
- **Resource Templates**: Parameterized queries for dynamic content
- **URI Schema Validation**: Proper MCP resource URI parsing and validation
- **Error Handling**: Comprehensive error responses for invalid requests
### 2. Guided Prompts Framework Implementation
#### Five Interactive Workflows
1. **`memory_review`**: Review and organize memories from specific time periods
2. **`memory_analysis`**: Analyze patterns, themes, and tag distributions
3. **`knowledge_export`**: Export memories in JSON, Markdown, or Text formats
4. **`memory_cleanup`**: Identify and remove duplicate or outdated memories
5. **`learning_session`**: Store structured learning notes with automatic categorization
#### Technical Features
- **Proper Argument Schemas**: Each prompt includes comprehensive parameter validation
- **Interactive Workflows**: Step-by-step guided processes for complex operations
- **Context-Aware Processing**: Prompts understand user intent and provide relevant guidance
- **Result Formatting**: Structured outputs appropriate for each workflow type
### 3. Progress Tracking System Implementation
#### Real-time Operation Monitoring
- **Progress Notifications**: MCP-compliant progress updates with percentage completion
- **Operation IDs**: Unique identifiers for tracking concurrent tasks
- **Step-by-step Updates**: Detailed progress information for long-running operations
- **Enhanced Operations**: `delete_by_tags` and `dashboard_optimize_db` with progress tracking
#### Protocol Compliance
```python
# Example progress notification structure:
{
"method": "notifications/progress",
"params": {
"progressToken": "operation_id_123",
"progress": 0.65, # 65% complete
"total": 100
}
}
```
## Architectural Enhancements
### 1. Storage Backend Extensions
- **Extended `MemoryStorage` base class** with helper methods for resources
- **New helper methods**: `get_stats()`, `get_all_tags()`, `get_recent_memories()`
- **Backward compatibility**: All existing storage implementations continue to work
### 2. Model Enhancements
- **Enhanced `Memory` model** with `to_dict()` method for JSON serialization
- **Enhanced `MemoryQueryResult` model** with structured data conversion
- **Type safety improvements** with comprehensive type hints
### 3. Server Architecture Improvements
- **Added progress tracking state management** to MemoryServer
- **New `send_progress_notification()` method** for real-time updates
- **Enhanced initialization** with progress tracking capabilities
- **Thread-safe operation tracking** for concurrent operations
## Code Quality Improvements
### 1. Implementation Standards
- **Clean Code Principles**: Modular, well-documented implementations
- **Type Safety**: Comprehensive type hints throughout new code
- **Error Handling**: Robust error handling with specific exception types
- **Documentation**: Comprehensive docstrings and inline comments
### 2. Testing Considerations
- **Backward Compatibility**: All existing tests continue to pass
- **New Functionality**: Implementation ready for comprehensive test coverage
- **Integration Testing**: New features integrate seamlessly with existing system
- **Performance**: No significant performance impact on core operations
## Version Management
### Semantic Versioning Update
- **Version Bump**: 4.0.1 → 4.1.0 (minor version for new features)
- **Changelog Documentation**: Comprehensive changelog entry with all new features
- **Release Notes**: Detailed documentation of MCP compliance achievements
### Configuration Management
- **Environment Variables**: No new environment variables required
- **Backward Compatibility**: Existing configurations continue to work unchanged
- **Optional Features**: All new features work with existing storage backends
## MCP Specification Compliance Matrix
### ✅ Fully Implemented Features
- **Resources**: URI-based access with templates ✅
- **Prompts**: Interactive workflows with argument schemas ✅
- **Progress Notifications**: Real-time operation tracking ✅
- **Tools**: Complete memory operation coverage ✅
- **Error Handling**: Proper MCP error responses ✅
- **JSON-RPC 2.0**: Full protocol compliance ✅
### 🔧 Enhanced Features
- **Resource Discovery**: Dynamic resource listing
- **Prompt Discovery**: Available workflow enumeration
- **Operation Monitoring**: Progress tracking for all applicable operations
- **Data Export**: Multiple format support (JSON, Markdown, Text)
- **Analytics**: Memory pattern analysis and insights
## User Experience Improvements
### 1. Client Interaction Enhancements
- **Discoverable Resources**: Clients can enumerate available memory resources
- **Guided Workflows**: Step-by-step assistance for complex operations
- **Real-time Feedback**: Progress updates for long-running operations
- **Structured Data**: Consistent JSON responses across all endpoints
### 2. Developer Experience
- **Complete API Coverage**: All MCP features fully implemented
- **Comprehensive Documentation**: Updated guides and examples
- **Backward Compatibility**: No breaking changes to existing implementations
- **Extended Capabilities**: New features enhance rather than replace existing functionality
## Performance Impact Analysis
### 1. Memory Usage
- **Minimal Impact**: New features use existing storage and processing infrastructure
- **Efficient Implementations**: Resource endpoints leverage existing database queries
- **Progress Tracking**: Lightweight state management without significant overhead
### 2. Response Times
- **Resource Endpoints**: Fast responses leveraging optimized database queries
- **Progress Notifications**: Asynchronous updates don't block main operations
- **Guided Prompts**: Interactive workflows maintain responsive user experience
## Future Development Foundation
### 1. Extensibility
- **Plugin Architecture**: Framework supports easy addition of new resources and prompts
- **Storage Backend Agnostic**: All new features work with both ChromaDB and SQLite-vec
- **Client Compatibility**: Full MCP compliance ensures compatibility with all MCP clients
### 2. Scalability Considerations
- **Concurrent Operations**: Progress tracking system supports multiple simultaneous operations
- **Resource Caching**: Framework ready for caching optimizations
- **Load Balancing**: Architecture supports horizontal scaling of MCP endpoints
## Documentation Updates
### 1. Technical Documentation
- **CHANGELOG.md**: Comprehensive v4.1.0 changelog with all new features
- **API Documentation**: Updated with new resources, prompts, and capabilities
- **Architecture Diagrams**: Reflect new MCP compliance features
### 2. User Guides
- **MCP Client Configuration**: Updated guides for new capabilities
- **Resource Usage Examples**: Practical examples for all new resource endpoints
- **Workflow Documentation**: Step-by-step guides for guided prompts
## Validation and Testing
### 1. Compliance Verification
- **MCP Protocol Testing**: All new features tested against MCP specification
- **Client Compatibility**: Verified compatibility with Claude Desktop and other MCP clients
- **Resource Validation**: All resource endpoints tested with various parameter combinations
### 2. Integration Testing
- **Existing Functionality**: All existing features continue to work unchanged
- **Storage Backend Compatibility**: New features work with both storage implementations
- **Cross-Platform Testing**: Verified functionality across different operating systems
## Success Metrics
### ✅ Implementation Completeness
- **100% MCP Resource Coverage**: All planned resource endpoints implemented
- **100% Guided Prompt Coverage**: All planned interactive workflows implemented
- **100% Progress Tracking Coverage**: Enhanced operations with real-time updates
- **100% Backward Compatibility**: No breaking changes to existing functionality
### ✅ Quality Assurance
- **Code Review**: All implementations reviewed for quality and compliance
- **Documentation Coverage**: Comprehensive documentation for all new features
- **Error Handling**: Robust error handling throughout new implementations
- **Type Safety**: Full type hints and validation for all new code
## Next Steps and Recommendations
### 1. Immediate Actions
- **Beta Testing**: Deploy v4.1.0 for community testing of new MCP features
- **Performance Monitoring**: Monitor resource endpoint performance under load
- **User Feedback**: Gather feedback on guided workflows and user experience
### 2. Future Enhancements
- **Additional Resources**: Consider additional resource endpoints based on user feedback
- **Enhanced Analytics**: Expand memory analysis capabilities in prompts
- **Performance Optimization**: Optimize resource endpoints based on usage patterns
- **Advanced Workflows**: Add more complex guided workflows for specialized use cases
## Conclusion
The v4.1.0 enhancement session successfully achieved **complete MCP specification compliance** for the MCP Memory Service. This represents a significant milestone in the project's evolution, transforming it from a basic memory storage system into a fully-featured, specification-compliant MCP server with advanced capabilities.
### Key Achievements Summary:
1. **Full MCP Compliance**: All MCP specification features implemented
2. **Enhanced User Experience**: Guided workflows and real-time progress tracking
3. **Architectural Excellence**: Clean, extensible implementation with backward compatibility
4. **Production Ready**: Robust error handling and comprehensive documentation
5. **Future-Proof**: Foundation for continued enhancement and scaling
This enhancement session establishes the MCP Memory Service as a **reference implementation** for MCP specification compliance while maintaining the flexibility and performance that made it successful in earlier versions.
---
**Tags**: mcp-compliance, resources-system, guided-prompts, progress-tracking, specification-implementation, architectural-enhancement, user-experience, backward-compatibility, production-ready, reference-implementation
**Metadata**:
- session_date: 2025-08-06
- version: 4.1.0
- repository: mcp-memory-service
- enhancement_type: mcp-specification-compliance
- implementation_scope: comprehensive
- backward_compatibility: full
- testing_status: ready-for-beta
- documentation_status: complete
```
--------------------------------------------------------------------------------
/tests/test_hybrid_cloudflare_limits.py:
--------------------------------------------------------------------------------
```python
#!/usr/bin/env python3
"""
Tests for Cloudflare limit handling in hybrid storage.
Tests cover:
- Pre-sync validation for oversized metadata
- Vector count limit enforcement
- Capacity monitoring and warnings
- Error handling for limit-related failures
"""
import asyncio
import pytest
import pytest_asyncio
import json
import tempfile
import os
import sys
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch
# Add src to path for imports
current_dir = Path(__file__).parent
src_dir = current_dir.parent / "src"
sys.path.insert(0, str(src_dir))
from mcp_memory_service.storage.hybrid import HybridMemoryStorage, BackgroundSyncService, SyncOperation
from mcp_memory_service.models.memory import Memory
import hashlib
class MockCloudflareWithLimits:
"""Mock Cloudflare storage that simulates limit errors."""
def __init__(self, **kwargs):
self.memories = {}
self.vector_count = 0
self.max_vectors = 100 # Low limit for testing
self.initialized = False
self.fail_on_limit = True
async def initialize(self):
self.initialized = True
async def store(self, memory: Memory):
# Check vector limit
if self.vector_count >= self.max_vectors and self.fail_on_limit:
raise Exception("413 Request Entity Too Large: Vector limit exceeded")
# Check metadata size (simulate 10KB limit)
if memory.metadata:
metadata_json = json.dumps(memory.metadata)
if len(metadata_json) > 10240: # 10KB
raise Exception("Metadata too large: exceeds 10KB limit")
self.memories[memory.content_hash] = memory
self.vector_count += 1
return True, "Stored"
async def delete(self, content_hash: str):
if content_hash in self.memories:
del self.memories[content_hash]
self.vector_count -= 1
return True, "Deleted"
async def get_stats(self):
return {
"total_memories": self.vector_count,
"storage_backend": "MockCloudflareWithLimits"
}
async def update_memory_metadata(self, content_hash: str, updates, preserve_timestamps=True):
return True, "Updated"
async def close(self):
pass
@pytest_asyncio.fixture
async def temp_db():
"""Create a temporary SQLite database."""
with tempfile.NamedTemporaryFile(suffix='.db', delete=False) as tmp:
db_path = tmp.name
yield db_path
if os.path.exists(db_path):
os.unlink(db_path)
@pytest_asyncio.fixture
async def hybrid_with_limits(temp_db):
"""Create hybrid storage with limit-aware mock Cloudflare."""
config = {
'api_token': 'test',
'account_id': 'test',
'vectorize_index': 'test',
'd1_database_id': 'test'
}
with patch('mcp_memory_service.storage.hybrid.CloudflareStorage', MockCloudflareWithLimits):
with patch('mcp_memory_service.storage.hybrid.CLOUDFLARE_VECTORIZE_MAX_VECTORS', 100):
with patch('mcp_memory_service.storage.hybrid.CLOUDFLARE_MAX_METADATA_SIZE_KB', 10):
storage = HybridMemoryStorage(
sqlite_db_path=temp_db,
cloudflare_config=config,
sync_interval=300,
batch_size=5
)
await storage.initialize()
yield storage
await storage.close()
class TestCloudflareMetadataLimits:
"""Test metadata size validation."""
@pytest.mark.asyncio
async def test_oversized_metadata_validation(self, hybrid_with_limits):
"""Test that oversized metadata is caught during validation."""
# Create memory with large metadata (> 10KB)
large_metadata = {"data": "x" * 11000} # Over 10KB when serialized
memory = Memory(
content="Test memory with large metadata",
content_hash=hashlib.sha256(b"test").hexdigest(),
tags=["test"],
metadata=large_metadata
)
# Validation should fail
is_valid, error = await hybrid_with_limits.sync_service.validate_memory_for_cloudflare(memory)
assert not is_valid
assert "exceeds Cloudflare limit" in error
@pytest.mark.asyncio
async def test_normal_metadata_passes_validation(self, hybrid_with_limits):
"""Test that normal-sized metadata passes validation."""
normal_metadata = {"key": "value", "index": 123}
memory = Memory(
content="Test memory with normal metadata",
content_hash=hashlib.sha256(b"test2").hexdigest(),
tags=["test"],
metadata=normal_metadata
)
# Validation should pass
is_valid, error = await hybrid_with_limits.sync_service.validate_memory_for_cloudflare(memory)
assert is_valid
assert error is None
class TestVectorCountLimits:
"""Test vector count limit handling."""
@pytest.mark.asyncio
async def test_vector_limit_detection(self, hybrid_with_limits):
"""Test detection when approaching vector count limit."""
# Simulate high vector count
hybrid_with_limits.sync_service.cloudflare_stats['vector_count'] = 95
# Check capacity should detect we're at 95% (critical)
capacity = await hybrid_with_limits.sync_service.check_cloudflare_capacity()
assert capacity['vector_usage_percent'] == 95.0
assert capacity['approaching_limits'] is True
assert len(capacity['warnings']) > 0
assert "CRITICAL" in capacity['warnings'][0]
@pytest.mark.asyncio
async def test_vector_limit_enforcement(self, hybrid_with_limits):
"""Test that sync stops when vector limit is reached."""
# Set vector count at limit
hybrid_with_limits.sync_service.cloudflare_stats['vector_count'] = 100
memory = Memory(
content="Memory that should be rejected",
content_hash=hashlib.sha256(b"rejected").hexdigest(),
tags=["test"]
)
# Validation should fail due to limit
is_valid, error = await hybrid_with_limits.sync_service.validate_memory_for_cloudflare(memory)
assert not is_valid
assert "vector limit" in error.lower()
class TestErrorHandling:
"""Test error handling for various limit scenarios."""
@pytest.mark.asyncio
async def test_limit_error_no_retry(self, hybrid_with_limits):
"""Test that limit errors are not retried."""
operation = SyncOperation(
operation='store',
memory=Memory(content="test", content_hash="hash123", tags=[])
)
# Simulate a limit error
error = Exception("413 Request Entity Too Large: Vector limit exceeded")
await hybrid_with_limits.sync_service._handle_sync_error(error, operation)
# Should not be added to retry queue
assert len(hybrid_with_limits.sync_service.failed_operations) == 0
# Should be marked as failed
assert hybrid_with_limits.sync_service.sync_stats['operations_failed'] == 1
@pytest.mark.asyncio
async def test_temporary_error_retry(self, hybrid_with_limits):
"""Test that temporary errors are retried."""
operation = SyncOperation(
operation='store',
memory=Memory(content="test", content_hash="hash456", tags=[]),
retries=0
)
# Simulate a temporary error
error = Exception("503 Service Temporarily Unavailable")
await hybrid_with_limits.sync_service._handle_sync_error(error, operation)
# Should be added to retry queue
assert len(hybrid_with_limits.sync_service.failed_operations) == 1
# Should not be marked as failed yet
assert hybrid_with_limits.sync_service.sync_stats['operations_failed'] == 0
@pytest.mark.asyncio
async def test_max_retries_reached(self, hybrid_with_limits):
"""Test that operations fail after max retries."""
operation = SyncOperation(
operation='store',
memory=Memory(content="test", content_hash="hash789", tags=[]),
retries=2, # Already retried twice
max_retries=3
)
# Simulate another temporary error
error = Exception("Connection timeout")
await hybrid_with_limits.sync_service._handle_sync_error(error, operation)
# Should not be added to retry queue (max retries reached)
assert len(hybrid_with_limits.sync_service.failed_operations) == 0
# Should be marked as failed
assert hybrid_with_limits.sync_service.sync_stats['operations_failed'] == 1
class TestCapacityMonitoring:
"""Test capacity monitoring and warnings."""
@pytest.mark.asyncio
async def test_capacity_warning_thresholds(self, hybrid_with_limits):
"""Test warning at 80% and critical at 95% thresholds."""
service = hybrid_with_limits.sync_service
# Test 50% - no warning
service.cloudflare_stats['vector_count'] = 50
capacity = await service.check_cloudflare_capacity()
assert not capacity['approaching_limits']
assert len(capacity['warnings']) == 0
# Test 80% - warning
service.cloudflare_stats['vector_count'] = 80
capacity = await service.check_cloudflare_capacity()
assert capacity['approaching_limits']
assert "WARNING" in capacity['warnings'][0]
# Test 95% - critical
service.cloudflare_stats['vector_count'] = 95
capacity = await service.check_cloudflare_capacity()
assert capacity['approaching_limits']
assert "CRITICAL" in capacity['warnings'][0]
@pytest.mark.asyncio
async def test_sync_status_includes_capacity(self, hybrid_with_limits):
"""Test that sync status includes capacity information."""
status = await hybrid_with_limits.sync_service.get_sync_status()
assert 'capacity' in status
assert 'vector_count' in status['capacity']
assert 'vector_limit' in status['capacity']
assert 'approaching_limits' in status['capacity']
assert 'warnings' in status['capacity']
class TestIntegrationScenarios:
"""Test complete scenarios with limit handling."""
@pytest.mark.asyncio
async def test_sync_stops_at_limit(self, hybrid_with_limits):
"""Test that sync gracefully handles reaching the limit."""
# Add memories up to the limit
memories = []
for i in range(105): # Try to exceed limit of 100
memory = Memory(
content=f"Memory {i}",
content_hash=hashlib.sha256(f"hash{i}".encode()).hexdigest(),
tags=["bulk"],
metadata={"index": i}
)
memories.append(memory)
# Process memories
successful = 0
failed = 0
for memory in memories:
operation = SyncOperation(operation='store', memory=memory)
# Validate first
is_valid, _ = await hybrid_with_limits.sync_service.validate_memory_for_cloudflare(memory)
if is_valid:
try:
await hybrid_with_limits.sync_service._process_single_operation(operation)
successful += 1
except Exception:
failed += 1
else:
failed += 1
# Should stop at or before the limit
assert successful <= 100
assert failed >= 5 # At least 5 should fail due to limit
@pytest.mark.asyncio
async def test_periodic_capacity_check(self, hybrid_with_limits):
"""Test that periodic sync checks capacity."""
# Set up near-limit scenario
hybrid_with_limits.sync_service.cloudflare_stats['vector_count'] = 85
# Mock the secondary's get_stats
async def mock_get_stats():
return {"total_memories": 85}
hybrid_with_limits.sync_service.secondary.get_stats = mock_get_stats
# Run periodic sync
await hybrid_with_limits.sync_service._periodic_sync()
# Should have detected approaching limits
assert hybrid_with_limits.sync_service.cloudflare_stats['approaching_limits']
assert len(hybrid_with_limits.sync_service.cloudflare_stats['limit_warnings']) > 0
if __name__ == "__main__":
pytest.main([__file__, "-v"])
```
--------------------------------------------------------------------------------
/tests/test_timestamp_preservation.py:
--------------------------------------------------------------------------------
```python
"""
Comprehensive tests for timestamp preservation during sync operations.
This test suite verifies that the fix for the timestamp regression bug
(where created_at was being reset during metadata sync) works correctly.
"""
import pytest
import pytest_asyncio
import time
import tempfile
import os
from datetime import datetime
from pathlib import Path
from mcp_memory_service.storage.sqlite_vec import SqliteVecMemoryStorage
from mcp_memory_service.models import Memory
@pytest_asyncio.fixture
async def storage():
"""Create a temporary SQLite storage for testing."""
# Create temporary database file
temp_dir = tempfile.mkdtemp()
db_path = os.path.join(temp_dir, "test.db")
storage = SqliteVecMemoryStorage(
db_path=db_path,
embedding_model="all-MiniLM-L6-v2"
)
await storage.initialize()
yield storage
# Cleanup
storage.close() # Not async
try:
os.remove(db_path)
os.rmdir(temp_dir)
except:
pass
@pytest.fixture
def old_memory():
"""Create a memory with an old timestamp (24 hours ago)."""
old_time = time.time() - 86400 # 24 hours ago
old_iso = datetime.utcfromtimestamp(old_time).isoformat() + "Z"
return Memory(
content="This is a test memory from yesterday",
content_hash="test_hash_old_memory",
tags=["test", "old"],
memory_type="note",
metadata={"original": True},
created_at=old_time,
created_at_iso=old_iso,
updated_at=old_time,
updated_at_iso=old_iso
)
@pytest.mark.asyncio
class TestTimestampPreservation:
"""Test suite for timestamp preservation during metadata updates."""
async def test_preserve_timestamps_true_only_updates_updated_at(self, storage, old_memory):
"""
Test that preserve_timestamps=True only updates updated_at,
leaving created_at unchanged.
"""
# Store memory with old timestamp
await storage.store(old_memory)
# Update metadata with preserve_timestamps=True (default)
updates = {
"tags": ["test", "updated"],
"metadata": {"updated": True}
}
success, _ = await storage.update_memory_metadata(
old_memory.content_hash,
updates,
preserve_timestamps=True
)
assert success
# Retrieve and verify timestamps
cursor = storage.conn.execute('''
SELECT created_at, created_at_iso, updated_at, updated_at_iso
FROM memories WHERE content_hash = ?
''', (old_memory.content_hash,))
row = cursor.fetchone()
created_at, created_at_iso, updated_at, updated_at_iso = row
# created_at should be preserved (within 1 second tolerance)
assert abs(created_at - old_memory.created_at) < 1.0, \
f"created_at changed! Expected {old_memory.created_at}, got {created_at}"
assert created_at_iso == old_memory.created_at_iso
# updated_at should be recent (within last 5 seconds)
now = time.time()
assert abs(updated_at - now) < 5.0, \
f"updated_at not updated! Expected ~{now}, got {updated_at}"
async def test_preserve_timestamps_false_without_source_preserves_created_at(self, storage, old_memory):
"""
Test that preserve_timestamps=False without providing timestamps
still preserves created_at (regression test for the bug).
"""
# Store memory with old timestamp
await storage.store(old_memory)
# Update metadata with preserve_timestamps=False but NO timestamps in updates
# This simulates the BUGGY behavior that was resetting created_at
updates = {
"tags": ["test", "synced"],
"metadata": {"synced": True}
}
success, _ = await storage.update_memory_metadata(
old_memory.content_hash,
updates,
preserve_timestamps=False
)
assert success
# Retrieve and verify timestamps
cursor = storage.conn.execute('''
SELECT created_at, created_at_iso, updated_at, updated_at_iso
FROM memories WHERE content_hash = ?
''', (old_memory.content_hash,))
row = cursor.fetchone()
created_at, created_at_iso, updated_at, updated_at_iso = row
# created_at should STILL be preserved (this is the fix!)
assert abs(created_at - old_memory.created_at) < 1.0, \
f"BUG: created_at was reset! Expected {old_memory.created_at}, got {created_at}"
assert created_at_iso == old_memory.created_at_iso, \
f"BUG: created_at_iso was reset! Expected {old_memory.created_at_iso}, got {created_at_iso}"
async def test_preserve_timestamps_false_with_source_uses_source_timestamps(self, storage, old_memory):
"""
Test that preserve_timestamps=False WITH source timestamps
uses the provided timestamps (for drift detection sync).
"""
# Store memory with old timestamp
await storage.store(old_memory)
# Simulate a sync from Cloudflare with newer metadata but original created_at
cloudflare_updated_at = time.time() - 3600 # 1 hour ago (newer than local)
cloudflare_updated_iso = datetime.utcfromtimestamp(cloudflare_updated_at).isoformat() + "Z"
updates = {
"tags": ["test", "synced-from-cloudflare"],
"metadata": {"source": "cloudflare"},
"created_at": old_memory.created_at, # Original creation time
"created_at_iso": old_memory.created_at_iso,
"updated_at": cloudflare_updated_at, # Newer update time
"updated_at_iso": cloudflare_updated_iso
}
success, _ = await storage.update_memory_metadata(
old_memory.content_hash,
updates,
preserve_timestamps=False
)
assert success
# Retrieve and verify timestamps
cursor = storage.conn.execute('''
SELECT created_at, created_at_iso, updated_at, updated_at_iso
FROM memories WHERE content_hash = ?
''', (old_memory.content_hash,))
row = cursor.fetchone()
created_at, created_at_iso, updated_at, updated_at_iso = row
# created_at should match the source (Cloudflare)
assert abs(created_at - old_memory.created_at) < 1.0, \
f"created_at not preserved from source! Expected {old_memory.created_at}, got {created_at}"
assert created_at_iso == old_memory.created_at_iso
# updated_at should match the source (Cloudflare)
assert abs(updated_at - cloudflare_updated_at) < 1.0, \
f"updated_at not from source! Expected {cloudflare_updated_at}, got {updated_at}"
assert updated_at_iso == cloudflare_updated_iso
async def test_drift_detection_scenario(self, storage, old_memory):
"""
Test the complete drift detection scenario:
1. Memory exists locally with old metadata
2. Cloudflare has newer metadata
3. Drift detection syncs metadata while preserving created_at
"""
# Store memory with old timestamp
await storage.store(old_memory)
# Simulate Cloudflare memory with newer metadata
cf_updated_at = time.time() - 1800 # 30 minutes ago
cf_updated_iso = datetime.utcfromtimestamp(cf_updated_at).isoformat() + "Z"
# This is what hybrid storage does during drift detection
cf_updates = {
'tags': ["test", "cloudflare-updated"],
'memory_type': "reference",
'metadata': {"updated_via": "cloudflare"},
'created_at': old_memory.created_at, # Preserve original
'created_at_iso': old_memory.created_at_iso,
'updated_at': cf_updated_at, # Use Cloudflare's update time
'updated_at_iso': cf_updated_iso,
}
success, _ = await storage.update_memory_metadata(
old_memory.content_hash,
cf_updates,
preserve_timestamps=False # Use Cloudflare timestamps
)
assert success
# Verify the sync preserved created_at but updated metadata
cursor = storage.conn.execute('''
SELECT created_at, updated_at, tags, memory_type, metadata
FROM memories WHERE content_hash = ?
''', (old_memory.content_hash,))
row = cursor.fetchone()
created_at, updated_at, tags, memory_type, metadata_str = row
# Timestamps
assert abs(created_at - old_memory.created_at) < 1.0, \
"Drift detection reset created_at!"
assert abs(updated_at - cf_updated_at) < 1.0, \
"Drift detection didn't use Cloudflare updated_at!"
# Metadata
assert tags == "test,cloudflare-updated"
assert memory_type == "reference"
async def test_multiple_syncs_preserve_original_created_at(self, storage, old_memory):
"""
Test that multiple sync operations (as would happen over time)
never reset the original created_at timestamp.
"""
# Store memory with old timestamp
await storage.store(old_memory)
original_created_at = old_memory.created_at
# Simulate 3 sync operations over time
for i in range(3):
sync_time = time.time() - (3600 * (3 - i)) # 3h, 2h, 1h ago
sync_iso = datetime.utcfromtimestamp(sync_time).isoformat() + "Z"
updates = {
'tags': ["test", f"sync-{i+1}"],
'metadata': {"sync_count": i + 1},
'created_at': original_created_at, # Always the original
'created_at_iso': old_memory.created_at_iso,
'updated_at': sync_time,
'updated_at_iso': sync_iso,
}
success, _ = await storage.update_memory_metadata(
old_memory.content_hash,
updates,
preserve_timestamps=False
)
assert success
# Verify created_at never changed
cursor = storage.conn.execute('''
SELECT created_at, created_at_iso
FROM memories WHERE content_hash = ?
''', (old_memory.content_hash,))
row = cursor.fetchone()
created_at, created_at_iso = row
assert abs(created_at - original_created_at) < 1.0, \
f"After {3} syncs, created_at changed! Expected {original_created_at}, got {created_at}"
assert created_at_iso == old_memory.created_at_iso
async def test_new_memory_store_sets_timestamps_correctly(self, storage):
"""
Test that storing a new memory without explicit timestamps
sets them correctly (current time).
"""
now_before = time.time()
memory = Memory(
content="New memory without explicit timestamps",
content_hash="test_hash_new"
)
await storage.store(memory)
now_after = time.time()
# Retrieve and verify timestamps
cursor = storage.conn.execute('''
SELECT created_at, updated_at
FROM memories WHERE content_hash = ?
''', (memory.content_hash,))
row = cursor.fetchone()
created_at, updated_at = row
# Both should be recent (between before and after)
assert now_before <= created_at <= now_after, \
f"created_at not set to current time: {created_at}"
assert now_before <= updated_at <= now_after, \
f"updated_at not set to current time: {updated_at}"
async def test_store_memory_with_explicit_timestamps_preserves_them(self, storage, old_memory):
"""
Test that storing a memory WITH explicit timestamps
(e.g., synced from Cloudflare) preserves those timestamps.
"""
await storage.store(old_memory)
# Retrieve and verify timestamps
cursor = storage.conn.execute('''
SELECT created_at, created_at_iso, updated_at, updated_at_iso
FROM memories WHERE content_hash = ?
''', (old_memory.content_hash,))
row = cursor.fetchone()
created_at, created_at_iso, updated_at, updated_at_iso = row
# All timestamps should match what was provided
assert abs(created_at - old_memory.created_at) < 1.0
assert created_at_iso == old_memory.created_at_iso
assert abs(updated_at - old_memory.updated_at) < 1.0
assert updated_at_iso == old_memory.updated_at_iso
if __name__ == "__main__":
pytest.main([__file__, "-v"])
```
--------------------------------------------------------------------------------
/src/mcp_memory_service/discovery/mdns_service.py:
--------------------------------------------------------------------------------
```python
# Copyright 2024 Heinrich Krupp
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
mDNS service advertisement and discovery for MCP Memory Service.
This module provides classes to advertise the MCP Memory Service on the local
network using mDNS (Multicast DNS) and discover other MCP Memory Service instances.
"""
import asyncio
import logging
import socket
from typing import Dict, List, Optional, Callable, Any
from dataclasses import dataclass
from zeroconf import Zeroconf, ServiceInfo, ServiceBrowser, ServiceListener
from zeroconf.asyncio import AsyncZeroconf, AsyncServiceBrowser
from ..config import (
MDNS_SERVICE_NAME,
MDNS_SERVICE_TYPE,
MDNS_DISCOVERY_TIMEOUT,
HTTP_HOST,
HTTP_PORT,
HTTPS_ENABLED,
API_KEY,
SERVER_VERSION
)
logger = logging.getLogger(__name__)
@dataclass
class ServiceDetails:
"""Details of a discovered MCP Memory Service."""
name: str
host: str
port: int
https: bool
api_version: str
requires_auth: bool
service_info: ServiceInfo
@property
def url(self) -> str:
"""Get the service URL."""
protocol = "https" if self.https else "http"
return f"{protocol}://{self.host}:{self.port}"
@property
def api_url(self) -> str:
"""Get the API base URL."""
return f"{self.url}/api"
class ServiceAdvertiser:
"""Advertises MCP Memory Service via mDNS."""
def __init__(
self,
service_name: str = MDNS_SERVICE_NAME,
service_type: str = MDNS_SERVICE_TYPE,
host: str = HTTP_HOST,
port: int = HTTP_PORT,
https_enabled: bool = HTTPS_ENABLED,
api_key_required: bool = bool(API_KEY)
):
self.service_name = service_name
self.service_type = service_type
self.host = host
self.port = port
self.https_enabled = https_enabled
self.api_key_required = api_key_required
self._zeroconf: Optional[AsyncZeroconf] = None
self._service_info: Optional[ServiceInfo] = None
self._registered = False
def _get_local_ip(self) -> str:
"""Get the local IP address for service advertisement."""
try:
# Connect to a remote address to determine the local IP
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.connect(("8.8.8.8", 80))
local_ip = s.getsockname()[0]
return local_ip
except Exception:
# Fallback to localhost if unable to determine IP
return "127.0.0.1"
def _create_service_info(self) -> ServiceInfo:
"""Create ServiceInfo for mDNS advertisement."""
# Get local IP address
local_ip = self._get_local_ip()
# Create service properties
properties = {
'api_version': SERVER_VERSION.encode('utf-8'),
'https': str(self.https_enabled).encode('utf-8'),
'auth_required': str(self.api_key_required).encode('utf-8'),
'api_path': b'/api',
'sse_path': b'/api/events',
'docs_path': b'/api/docs'
}
# Create unique service name
full_service_name = f"{self.service_name}.{self.service_type}"
service_info = ServiceInfo(
type_=self.service_type,
name=full_service_name,
addresses=[socket.inet_aton(local_ip)],
port=self.port,
properties=properties,
server=f"{self.service_name.replace(' ', '-').lower()}.local."
)
logger.info(f"Created service info: {full_service_name} at {local_ip}:{self.port}")
return service_info
async def start(self) -> bool:
"""Start advertising the service via mDNS."""
if self._registered:
logger.warning("Service is already being advertised")
return True
try:
self._zeroconf = AsyncZeroconf()
self._service_info = self._create_service_info()
await self._zeroconf.async_register_service(self._service_info)
self._registered = True
logger.info(f"mDNS service advertisement started for {self.service_name}")
return True
except Exception as e:
logger.error(f"Failed to start mDNS service advertisement: {e}")
return False
async def stop(self) -> None:
"""Stop advertising the service."""
if not self._registered:
return
try:
if self._zeroconf and self._service_info:
await self._zeroconf.async_unregister_service(self._service_info)
await self._zeroconf.async_close()
self._registered = False
self._zeroconf = None
self._service_info = None
logger.info(f"mDNS service advertisement stopped for {self.service_name}")
except Exception as e:
logger.error(f"Error stopping mDNS service advertisement: {e}")
def __del__(self):
"""Cleanup on deletion."""
if self._registered and self._zeroconf:
# Note: This should be handled by explicit stop() calls
logger.warning("ServiceAdvertiser being deleted while still registered")
class DiscoveryListener(ServiceListener):
"""Listener for MCP Memory Service discoveries."""
def __init__(self, callback: Optional[Callable[[ServiceDetails], None]] = None):
self.callback = callback
self.services: Dict[str, ServiceDetails] = {}
def add_service(self, zc: Zeroconf, type_: str, name: str) -> None:
"""Called when a service is discovered."""
info = zc.get_service_info(type_, name)
if info:
try:
service_details = self._parse_service_info(info)
self.services[name] = service_details
logger.info(f"Discovered MCP Memory Service: {service_details.name} at {service_details.url}")
if self.callback:
self.callback(service_details)
except Exception as e:
logger.error(f"Error parsing discovered service {name}: {e}")
def remove_service(self, zc: Zeroconf, type_: str, name: str) -> None:
"""Called when a service is removed."""
if name in self.services:
service_details = self.services.pop(name)
logger.info(f"MCP Memory Service removed: {service_details.name}")
def update_service(self, zc: Zeroconf, type_: str, name: str) -> None:
"""Called when a service is updated."""
info = zc.get_service_info(type_, name)
if info:
try:
service_details = self._parse_service_info(info)
self.services[name] = service_details
logger.info(f"MCP Memory Service updated: {service_details.name}")
if self.callback:
self.callback(service_details)
except Exception as e:
logger.error(f"Error parsing updated service {name}: {e}")
def _parse_service_info(self, info: ServiceInfo) -> ServiceDetails:
"""Parse ServiceInfo into ServiceDetails."""
# Get host address
host = socket.inet_ntoa(info.addresses[0]) if info.addresses else "localhost"
# Parse properties
properties = info.properties or {}
https = properties.get(b'https', b'false').decode('utf-8').lower() == 'true'
api_version = properties.get(b'api_version', b'unknown').decode('utf-8')
requires_auth = properties.get(b'auth_required', b'false').decode('utf-8').lower() == 'true'
# Extract service name from full service name
service_name = info.name.replace(f".{info.type}", "")
return ServiceDetails(
name=service_name,
host=host,
port=info.port,
https=https,
api_version=api_version,
requires_auth=requires_auth,
service_info=info
)
class ServiceDiscovery:
"""Discovers MCP Memory Services on the local network."""
def __init__(
self,
service_type: str = MDNS_SERVICE_TYPE,
discovery_timeout: int = MDNS_DISCOVERY_TIMEOUT
):
self.service_type = service_type
self.discovery_timeout = discovery_timeout
self._zeroconf: Optional[AsyncZeroconf] = None
self._browser: Optional[AsyncServiceBrowser] = None
self._listener: Optional[DiscoveryListener] = None
self._discovering = False
async def discover_services(
self,
callback: Optional[Callable[[ServiceDetails], None]] = None
) -> List[ServiceDetails]:
"""Discover MCP Memory Services on the network."""
if self._discovering:
logger.warning("Discovery is already in progress")
return list(self._listener.services.values()) if self._listener else []
services = []
try:
self._zeroconf = AsyncZeroconf()
self._listener = DiscoveryListener(callback)
self._browser = AsyncServiceBrowser(
self._zeroconf.zeroconf,
self.service_type,
handlers=[self._listener]
)
self._discovering = True
logger.info(f"Starting mDNS discovery for {self.service_type}")
# Wait for discovery timeout
await asyncio.sleep(self.discovery_timeout)
services = list(self._listener.services.values())
logger.info(f"Discovered {len(services)} MCP Memory Services")
except Exception as e:
logger.error(f"Error during service discovery: {e}")
finally:
await self.stop_discovery()
return services
async def start_continuous_discovery(
self,
callback: Callable[[ServiceDetails], None]
) -> bool:
"""Start continuous service discovery."""
if self._discovering:
logger.warning("Discovery is already in progress")
return False
try:
self._zeroconf = AsyncZeroconf()
self._listener = DiscoveryListener(callback)
self._browser = AsyncServiceBrowser(
self._zeroconf.zeroconf,
self.service_type,
handlers=[self._listener]
)
self._discovering = True
logger.info(f"Started continuous mDNS discovery for {self.service_type}")
return True
except Exception as e:
logger.error(f"Error starting continuous service discovery: {e}")
return False
async def stop_discovery(self) -> None:
"""Stop service discovery."""
if not self._discovering:
return
try:
if self._browser:
await self._browser.async_cancel()
if self._zeroconf:
await self._zeroconf.async_close()
self._discovering = False
self._browser = None
self._zeroconf = None
self._listener = None
logger.info("mDNS service discovery stopped")
except Exception as e:
logger.error(f"Error stopping service discovery: {e}")
def get_discovered_services(self) -> List[ServiceDetails]:
"""Get currently discovered services."""
if self._listener:
return list(self._listener.services.values())
return []
def __del__(self):
"""Cleanup on deletion."""
if self._discovering:
logger.warning("ServiceDiscovery being deleted while still discovering")
```
--------------------------------------------------------------------------------
/src/mcp_memory_service/web/api/manage.py:
--------------------------------------------------------------------------------
```python
# Copyright 2024 Heinrich Krupp
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Management endpoints for the HTTP interface.
Provides memory maintenance, bulk operations, and system management tools.
"""
import logging
from typing import List, Optional, Dict, Any, TYPE_CHECKING
from datetime import datetime
from fastapi import APIRouter, HTTPException, Depends, Query
from pydantic import BaseModel, Field
from ...storage.base import MemoryStorage
from ...config import OAUTH_ENABLED
from ..dependencies import get_storage
from .memories import MemoryResponse, memory_to_response
# OAuth authentication imports (conditional)
if OAUTH_ENABLED or TYPE_CHECKING:
from ..oauth.middleware import require_write_access, AuthenticationResult
else:
# Provide type stubs when OAuth is disabled
AuthenticationResult = None
require_write_access = None
router = APIRouter()
logger = logging.getLogger(__name__)
# Request/Response Models
class BulkDeleteRequest(BaseModel):
"""Request model for bulk delete operations."""
tag: Optional[str] = Field(None, description="Delete all memories with this tag")
before_date: Optional[str] = Field(None, description="Delete memories before this date (YYYY-MM-DD)")
memory_type: Optional[str] = Field(None, description="Delete memories of this type")
confirm_count: Optional[int] = Field(None, description="Confirmation of number of memories to delete")
class TagManagementRequest(BaseModel):
"""Request model for tag management operations."""
operation: str = Field(..., description="Operation: 'rename', 'merge', or 'delete'")
old_tag: str = Field(..., description="Original tag name")
new_tag: Optional[str] = Field(None, description="New tag name (for rename/merge)")
confirm_count: Optional[int] = Field(None, description="Confirmation count for destructive operations")
class BulkOperationResponse(BaseModel):
"""Response model for bulk operations."""
success: bool
message: str
affected_count: int
operation: str
class TagStatsResponse(BaseModel):
"""Response model for tag statistics."""
tag: str
count: int
last_used: Optional[float]
memory_types: List[str]
class TagStatsListResponse(BaseModel):
"""Response model for tag statistics list."""
tags: List[TagStatsResponse]
total_tags: int
class SystemOperationRequest(BaseModel):
"""Request model for system operations."""
operation: str = Field(..., description="Operation: 'cleanup_duplicates', 'optimize_db', 'rebuild_index'")
@router.post("/bulk-delete", response_model=BulkOperationResponse, tags=["management"])
async def bulk_delete_memories(
request: BulkDeleteRequest,
storage: MemoryStorage = Depends(get_storage),
user: AuthenticationResult = Depends(require_write_access) if OAUTH_ENABLED else None
):
"""
Perform bulk delete operations on memories.
Supports deletion by tag, date range, or memory type.
Requires confirmation count for safety.
"""
try:
affected_count = 0
operation_desc = ""
# Validate that at least one filter is provided
if not any([request.tag, request.before_date, request.memory_type]):
raise HTTPException(
status_code=400,
detail="At least one filter (tag, before_date, or memory_type) must be specified"
)
# Count memories that would be affected
if request.tag:
# Count memories with this tag
if hasattr(storage, 'count_memories_by_tag'):
affected_count = await storage.count_memories_by_tag([request.tag])
else:
# Fallback: search and count
tag_memories = await storage.search_by_tag([request.tag])
affected_count = len(tag_memories)
operation_desc = f"Delete memories with tag '{request.tag}'"
elif request.before_date:
# Count memories before date
try:
before_dt = datetime.fromisoformat(request.before_date)
before_ts = before_dt.timestamp()
# This would need a method to count by date range
# For now, we'll estimate or implement a simple approach
affected_count = 0 # Placeholder
operation_desc = f"Delete memories before {request.before_date}"
except ValueError:
raise HTTPException(status_code=400, detail="Invalid date format. Use YYYY-MM-DD")
elif request.memory_type:
# Count memories by type
if hasattr(storage, 'count_all_memories'):
affected_count = await storage.count_all_memories(memory_type=request.memory_type)
else:
affected_count = 0 # Placeholder
operation_desc = f"Delete memories of type '{request.memory_type}'"
# Safety check: require confirmation count
if request.confirm_count is not None and request.confirm_count != affected_count:
raise HTTPException(
status_code=400,
detail=f"Confirmation count mismatch. Expected {affected_count}, got {request.confirm_count}"
)
# Perform the deletion
success = False
message = ""
if request.tag:
if hasattr(storage, 'delete_by_tag'):
success_count, message = await storage.delete_by_tag(request.tag)
success = success_count > 0
affected_count = success_count
else:
raise HTTPException(status_code=501, detail="Tag-based deletion not supported by storage backend")
elif request.before_date:
# Implement date-based deletion
# This would need to be implemented in the storage layer
raise HTTPException(status_code=501, detail="Date-based bulk deletion not yet implemented")
elif request.memory_type:
# Implement type-based deletion
# This would need to be implemented in the storage layer
raise HTTPException(status_code=501, detail="Type-based bulk deletion not yet implemented")
return BulkOperationResponse(
success=success,
message=message or f"Successfully deleted {affected_count} memories",
affected_count=affected_count,
operation=operation_desc
)
except HTTPException:
raise
except Exception as e:
logger.error(f"Bulk delete failed: {str(e)}")
raise HTTPException(status_code=500, detail=f"Bulk delete operation failed: {str(e)}")
@router.post("/cleanup-duplicates", response_model=BulkOperationResponse, tags=["management"])
async def cleanup_duplicates(
storage: MemoryStorage = Depends(get_storage),
user: AuthenticationResult = Depends(require_write_access) if OAUTH_ENABLED else None
):
"""
Clean up duplicate memories in the database.
Removes duplicate entries based on content hash and merges metadata.
"""
try:
if hasattr(storage, 'cleanup_duplicates'):
count, message = await storage.cleanup_duplicates()
return BulkOperationResponse(
success=count > 0,
message=message,
affected_count=count,
operation="cleanup_duplicates"
)
else:
raise HTTPException(status_code=501, detail="Duplicate cleanup not supported by storage backend")
except HTTPException:
raise
except Exception as e:
logger.error(f"Duplicate cleanup failed: {str(e)}")
raise HTTPException(status_code=500, detail=f"Duplicate cleanup failed: {str(e)}")
@router.get("/tags/stats", response_model=TagStatsListResponse, tags=["management"])
async def get_tag_statistics(
storage: MemoryStorage = Depends(get_storage),
user: AuthenticationResult = Depends(require_write_access) if OAUTH_ENABLED else None
):
"""
Get detailed statistics for all tags.
Returns tag usage counts, last usage times, and associated memory types.
"""
try:
# Get all tags with counts
if hasattr(storage, 'get_all_tags_with_counts'):
tag_data = await storage.get_all_tags_with_counts()
# For now, provide basic tag stats without additional queries
# TODO: Implement efficient batch queries in storage layer for last_used and memory_types
enhanced_tags = []
for tag_item in tag_data:
enhanced_tags.append(TagStatsResponse(
tag=tag_item["tag"],
count=tag_item["count"],
last_used=None, # Would need efficient batch query
memory_types=[] # Would need efficient batch query
))
return TagStatsListResponse(
tags=enhanced_tags,
total_tags=len(enhanced_tags)
)
else:
raise HTTPException(status_code=501, detail="Tag statistics not supported by storage backend")
except HTTPException:
raise
except Exception as e:
logger.error(f"Failed to get tag statistics: {str(e)}")
raise HTTPException(status_code=500, detail=f"Failed to get tag statistics: {str(e)}")
@router.put("/tags/{old_tag}", response_model=BulkOperationResponse, tags=["management"])
async def rename_tag(
old_tag: str,
new_tag: str,
confirm_count: Optional[int] = Query(None, description="Confirmation count"),
storage: MemoryStorage = Depends(get_storage),
user: AuthenticationResult = Depends(require_write_access) if OAUTH_ENABLED else None
):
"""
Rename a tag across all memories.
Updates all memories that have the old tag to use the new tag instead.
"""
try:
# Count memories with this tag
if hasattr(storage, 'count_memories_by_tag'):
affected_count = await storage.count_memories_by_tag([old_tag])
else:
tag_memories = await storage.search_by_tag([old_tag])
affected_count = len(tag_memories)
# Safety check
if confirm_count is not None and confirm_count != affected_count:
raise HTTPException(
status_code=400,
detail=f"Confirmation count mismatch. Expected {affected_count}, got {confirm_count}"
)
# Implement tag renaming (this would need to be implemented in storage layer)
# For now, return not implemented
raise HTTPException(status_code=501, detail="Tag renaming not yet implemented")
except HTTPException:
raise
except Exception as e:
logger.error(f"Tag rename failed: {str(e)}")
raise HTTPException(status_code=500, detail=f"Tag rename failed: {str(e)}")
@router.post("/system/{operation}", response_model=BulkOperationResponse, tags=["management"])
async def perform_system_operation(
operation: str,
storage: MemoryStorage = Depends(get_storage),
user: AuthenticationResult = Depends(require_write_access) if OAUTH_ENABLED else None
):
"""
Perform system maintenance operations.
Supported operations: cleanup_duplicates, optimize_db, rebuild_index
"""
try:
if operation == "cleanup_duplicates":
return await cleanup_duplicates(storage, user)
elif operation == "optimize_db":
# Database optimization (would need storage-specific implementation)
raise HTTPException(status_code=501, detail="Database optimization not yet implemented")
elif operation == "rebuild_index":
# Rebuild search indexes (would need storage-specific implementation)
raise HTTPException(status_code=501, detail="Index rebuilding not yet implemented")
else:
raise HTTPException(status_code=400, detail=f"Unknown operation: {operation}")
except HTTPException:
raise
except Exception as e:
logger.error(f"System operation {operation} failed: {str(e)}")
raise HTTPException(status_code=500, detail=f"System operation failed: {str(e)}")
```
--------------------------------------------------------------------------------
/docs/development/pr-review-guide.md:
--------------------------------------------------------------------------------
```markdown
# Pull Request Review Guide
This guide provides reviewers with a structured checklist for evaluating pull requests. It ensures consistent review quality and helps catch issues before merging.
## Review Workflow
1. **Initial Triage** (2 minutes) - Check PR template compliance
2. **Code Review** (10-30 minutes) - Review changes for quality and correctness
3. **Testing Verification** (5-15 minutes) - Validate tests and coverage
4. **Documentation Check** (5 minutes) - Ensure docs are updated
5. **Gemini Review** (optional) - Use `/gemini review` for additional analysis
6. **Approval or Request Changes** - Provide clear, actionable feedback
---
## 1. PR Template Compliance
### ✅ Description Quality
- [ ] **Summary Section Present**
- Clear description of what changes and why
- Reference to related issue(s): "Fixes #123", "Closes #456"
- Breaking changes clearly marked
- [ ] **Changes Section Complete**
- Bullet list of specific changes
- Technical details for reviewers
- Impact on existing functionality documented
- [ ] **Testing Section Detailed**
- Test strategy explained
- Manual testing steps included (if applicable)
- Test coverage metrics (if changed)
- [ ] **Screenshots/Examples** (if UI/API changes)
- Before/after screenshots for UI changes
- API request/response examples for new endpoints
- CLI output for new commands
### ✅ Metadata
- [ ] **Labels Applied**
- `bug`, `feature`, `enhancement`, `docs`, `performance`, etc.
- `breaking-change` if API/behavior changes
- `needs-release-notes` if user-visible changes
- [ ] **Milestone Set** (if applicable)
- Target release version assigned
- Aligns with project roadmap
- [ ] **Reviewers Assigned**
- At least one maintainer requested
- Subject matter experts tagged (if specialized change)
---
## 2. Code Review Standards
### ✅ Type Safety & Documentation
- [ ] **Type Hints Present**
```python
# ✅ Good
async def store_memory(
content: str,
tags: Optional[List[str]] = None,
metadata: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
# ❌ Bad - no type hints
async def store_memory(content, tags=None, metadata=None):
```
- [ ] **Docstrings Complete**
- All public functions/classes documented
- Google-style docstrings with Args/Returns/Raises
- Complex logic explained
- [ ] **Async Patterns Correct**
- `async def` for I/O operations
- `await` used for all async calls
- No blocking calls in async functions
### ✅ Error Handling
- [ ] **Specific Exception Types**
```python
# ✅ Good
try:
result = await storage.store(memory)
except StorageError as e:
logger.error(f"Failed to store memory: {e}")
raise MemoryServiceError(f"Storage operation failed: {e}") from e
# ❌ Bad - bare except
try:
result = await storage.store(memory)
except:
pass # Silently fails
```
- [ ] **Error Messages Helpful**
- Include context (what operation failed)
- Suggest remediation if possible
- Don't expose sensitive information
- [ ] **Logging Appropriate**
- Errors logged with full context
- Debug logging for troubleshooting
- No secrets logged
### ✅ Performance Considerations
- [ ] **Database Operations Efficient**
- Batch operations where possible
- Indexes used for filters
- No N+1 query patterns
- [ ] **Caching Appropriate**
- Global caches (models, embeddings) reused
- Cache invalidation handled correctly
- Memory leaks prevented
- [ ] **Async Operations Optimal**
- Concurrent operations where safe
- `asyncio.gather()` for parallel tasks
- Proper timeout handling
### ✅ Security
- [ ] **Input Validation**
- All user inputs validated
- SQL injection prevented (parameterized queries)
- Path traversal prevented (for file operations)
- [ ] **Secrets Management**
- No hardcoded credentials
- Environment variables for sensitive config
- API keys redacted in logs
- [ ] **Authentication/Authorization**
- API key validation for HTTP endpoints
- MCP protocol security maintained
- No bypass vulnerabilities
---
## 3. Testing Verification
### ✅ Test Coverage
- [ ] **Tests Exist for New Code**
- Unit tests for new functions/classes
- Integration tests for API changes
- Regression tests if fixing a bug
- [ ] **Test Quality**
- Tests are readable and maintainable
- Mock external dependencies (HTTP, DB, etc.)
- Test edge cases and error conditions
- [ ] **Coverage Metrics** (aim for >80%)
```bash
pytest --cov=mcp_memory_service tests/
# Check coverage report for changed files
```
### ✅ Test Execution
- [ ] **All Tests Pass**
- Local: `pytest tests/`
- CI: All GitHub Actions workflows green
- No flaky tests introduced
- [ ] **Manual Testing** (if complex change)
- Reviewer reproduces test scenarios
- Manual verification of user-facing features
- Platform-specific testing (if applicable)
### ✅ Regression Prevention
- [ ] **Existing Tests Still Pass**
- No tests removed or disabled without justification
- Test fixtures updated if data model changed
- [ ] **Performance Tests** (if performance-critical)
- Benchmarks show no degradation
- Scalability verified (e.g., 10K+ memories)
---
## 4. Documentation Updates
### ✅ Code Documentation
- [ ] **CLAUDE.md Updated** (if workflow changes)
- New commands/scripts documented
- Essential commands section updated
- Configuration examples added
- [ ] **CHANGELOG.md Updated**
- Entry in appropriate section (Added/Fixed/Changed)
- Follows [Keep a Changelog](https://keepachangelog.com/) format
- Breaking changes marked clearly
- [ ] **API Documentation** (if API changes)
- New endpoints documented
- Request/response examples provided
- Error codes and messages documented
### ✅ User-Facing Documentation
- [ ] **README.md** (if setup/installation changes)
- Installation steps updated
- Configuration examples current
- Troubleshooting tips added
- [ ] **Wiki Pages** (if major feature)
- Detailed guide created/updated
- Examples and use cases provided
- Cross-linked to related docs
### ✅ Migration Guides
- [ ] **Breaking Changes Documented**
- Migration path clearly explained
- Before/after code examples
- Database migration scripts (if applicable)
- [ ] **Deprecation Notices**
- Timeline specified (e.g., "removed in v9.0.0")
- Alternatives recommended
- Warnings added to deprecated code
---
## 5. Gemini Review Integration
### When to Use `/gemini review`
**Recommended for:**
- Large PRs (>500 lines changed)
- Complex algorithm changes
- Security-critical code
- Performance optimizations
- First-time contributors
**How to Use:**
1. Comment `/gemini review` on the PR
2. Wait ~1 minute for Gemini analysis
3. Review Gemini's findings alongside manual review
4. Address valid concerns, dismiss false positives
### Gemini Review Workflow
**Iteration Cycle:**
1. Contributor pushes changes
2. Maintainer comments with code review feedback
3. `/gemini review` for automated analysis
4. Wait 1 minute for Gemini response
5. Repeat until both human and Gemini reviews pass
**Gemini Strengths:**
- Catches common anti-patterns
- Identifies potential security issues
- Suggests performance improvements
- Validates test coverage
**Gemini Limitations:**
- May flag project-specific patterns as issues
- Context awareness limited (doesn't know full codebase)
- Final decision always with human reviewers
---
## 6. Merge Criteria
### ✅ Must-Have Before Merge
**Code Quality:**
- [ ] All reviewer feedback addressed
- [ ] No unresolved conversations
- [ ] Code follows project style guide
**Testing:**
- [ ] All tests pass (local + CI)
- [ ] New code has adequate test coverage
- [ ] Manual testing completed (if applicable)
**Documentation:**
- [ ] CHANGELOG.md updated
- [ ] User-facing docs updated
- [ ] Breaking changes documented with migration path
**Process:**
- [ ] Branch up-to-date with `main`
- [ ] No merge conflicts
- [ ] Commits follow semantic format
### ✅ Approval Process
**Required Approvals:**
- 1 maintainer approval minimum
- 2 approvals for breaking changes
- Security team approval for security-related changes
**Before Approving:**
- [ ] Reviewer has actually read the code (not just glanced)
- [ ] Tests have been run locally or CI verified
- [ ] Documentation checked for accuracy
### ✅ Merge Method
**Use "Squash and Merge" when:**
- Multiple small commits (WIP, fix typos, etc.)
- Commit history is messy
- Single logical change
**Use "Create a Merge Commit" when:**
- Multiple distinct features in PR
- Each commit is meaningful and well-documented
- Preserving contributor attribution important
**Never use "Rebase and Merge"** (causes issues with CI history)
---
## 7. Common Review Pitfalls
### ❌ Issues to Watch For
**Performance:**
- N+1 queries (loop calling DB for each item)
- Synchronous operations in async code
- Memory leaks (unclosed connections, large caches)
**Security:**
- SQL injection (string concatenation in queries)
- Path traversal (user input in file paths)
- Secrets in code/logs
**Error Handling:**
- Bare `except:` clauses
- Ignoring errors silently
- Cryptic error messages
**Testing:**
- Tests that don't actually test anything
- Flaky tests (timing-dependent, random failures)
- Missing edge cases
**Documentation:**
- Outdated examples
- Missing API documentation
- Breaking changes not highlighted
---
## 8. Providing Effective Feedback
### ✅ Good Feedback Practices
**Be Specific:**
```markdown
# ✅ Good
This function could cause database locks if called concurrently.
Consider adding `PRAGMA busy_timeout=15000` before connection.
# ❌ Bad
This might have issues.
```
**Provide Examples:**
```markdown
# ✅ Good
Consider using async context manager:
\`\`\`python
async with storage.transaction():
await storage.store(memory)
\`\`\`
# ❌ Bad
Use a transaction here.
```
**Distinguish Required vs. Optional:**
```markdown
# ✅ Good
**Required:** Add type hints to this function.
**Optional (nit):** Consider renaming `tmp` to `temporary_result` for clarity.
# ❌ Bad
Fix these things... [list of both critical and trivial items mixed]
```
**Be Constructive:**
```markdown
# ✅ Good
This implementation works but may be slow with large datasets.
Could we batch the operations? See `batch_store()` in storage.py for an example.
# ❌ Bad
This is terrible, completely wrong approach.
```
### ✅ Review Comment Structure
**For Issues:**
1. State the problem clearly
2. Explain why it's a problem
3. Suggest a solution (or ask for discussion)
4. Link to relevant docs/examples
**For Nitpicks:**
- Prefix with `nit:` or `optional:`
- Don't block merge on nitpicks
- Focus on critical issues first
**For Questions:**
- Ask for clarification on complex logic
- Request comments/docs if unclear
- Verify assumptions
---
## 9. Review Checklist Summary
Copy this checklist into your PR review comment:
```markdown
## Review Checklist
### Template Compliance
- [ ] Description complete with issue references
- [ ] Testing section detailed
- [ ] Labels and milestone set
### Code Quality
- [ ] Type hints and docstrings present
- [ ] Error handling robust
- [ ] Performance considerations addressed
- [ ] Security reviewed (input validation, secrets)
### Testing
- [ ] Tests exist and pass
- [ ] Coverage adequate (>80% for changed code)
- [ ] Manual testing completed (if applicable)
### Documentation
- [ ] CHANGELOG.md updated
- [ ] CLAUDE.md updated (if workflow changes)
- [ ] User-facing docs updated
- [ ] Breaking changes documented
### Process
- [ ] Branch up-to-date with main
- [ ] All CI checks passing
- [ ] Gemini review completed (if applicable)
**Approval Decision:** [ ] Approve | [ ] Request Changes | [ ] Comment
```
---
## 10. Resources
**Project Documentation:**
- [CONTRIBUTING.md](../../CONTRIBUTING.md) - Contribution guidelines
- [CLAUDE.md](../../CLAUDE.md) - Development workflow
- [Release Checklist](release-checklist.md) - Pre-release testing
**External Resources:**
- [Python Type Hints](https://docs.python.org/3/library/typing.html)
- [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html)
- [Keep a Changelog](https://keepachangelog.com/)
- [Semantic Versioning](https://semver.org/)
**Tools:**
- [Gemini Code Assist for GitHub](https://cloud.google.com/products/gemini/code-assist)
- [MCP Inspector](https://github.com/modelcontextprotocol/inspector) - Test MCP protocol compliance
- [pytest-cov](https://pytest-cov.readthedocs.io/) - Coverage reporting
---
**Last Updated:** 2025-11-05
**Version:** 1.0
**Related:** [Issue Management Guide](issue-management.md), [Release Checklist](release-checklist.md)
```
--------------------------------------------------------------------------------
/src/mcp_memory_service/ingestion/csv_loader.py:
--------------------------------------------------------------------------------
```python
# Copyright 2024 Heinrich Krupp
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
CSV document loader for tabular data files.
"""
import csv
import logging
from pathlib import Path
from typing import AsyncGenerator, Dict, Any, List, Optional
import asyncio
import io
from .base import DocumentLoader, DocumentChunk
from .chunker import TextChunker, ChunkingStrategy
logger = logging.getLogger(__name__)
class CSVLoader(DocumentLoader):
"""
Document loader for CSV data files.
Features:
- Automatic delimiter and header detection
- Converts rows to text with column context
- Handles large files with row-based chunking
- Preserves table structure in metadata
"""
def __init__(self, chunk_size: int = 1000, chunk_overlap: int = 200):
"""
Initialize CSV loader.
Args:
chunk_size: Target size for text chunks in characters
chunk_overlap: Number of characters to overlap between chunks
"""
super().__init__(chunk_size, chunk_overlap)
self.supported_extensions = ['csv']
self.chunker = TextChunker(ChunkingStrategy(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
respect_paragraph_boundaries=False, # CSV doesn't have paragraphs
respect_sentence_boundaries=False, # CSV doesn't have sentences
min_chunk_size=10 # Allow smaller chunks for tabular data
))
def can_handle(self, file_path: Path) -> bool:
"""
Check if this loader can handle the given CSV file.
Args:
file_path: Path to the file to check
Returns:
True if this loader can process the CSV file
"""
if not file_path.exists() or not file_path.is_file():
return False
extension = file_path.suffix.lower().lstrip('.')
return extension in self.supported_extensions
async def extract_chunks(self, file_path: Path, **kwargs) -> AsyncGenerator[DocumentChunk, None]:
"""
Extract text chunks from a CSV file.
Args:
file_path: Path to the CSV file
**kwargs: Additional options:
- has_header: Whether file has headers (auto-detected if not specified)
- delimiter: CSV delimiter (auto-detected if not specified)
- quotechar: Quote character (default: ")
- encoding: Text encoding (auto-detected if not specified)
- max_rows_per_chunk: Maximum rows to include in each chunk
- include_row_numbers: Whether to include row numbers in output
Yields:
DocumentChunk objects containing extracted text and metadata
Raises:
FileNotFoundError: If the CSV file doesn't exist
ValueError: If the CSV file can't be parsed or processed
"""
await self.validate_file(file_path)
has_header = kwargs.get('has_header', None) # Auto-detect if None
delimiter = kwargs.get('delimiter', None) # Auto-detect if None
quotechar = kwargs.get('quotechar', '"')
encoding = kwargs.get('encoding', None) # Auto-detect if None
max_rows_per_chunk = kwargs.get('max_rows_per_chunk', 50)
include_row_numbers = kwargs.get('include_row_numbers', True)
logger.info(f"Extracting chunks from CSV file: {file_path}")
try:
# Read CSV data
rows, detected_header, detected_delimiter, detected_encoding = await self._read_csv_file(
file_path, has_header, delimiter, quotechar, encoding
)
if not rows:
logger.warning(f"CSV file {file_path} appears to be empty")
return
# Prepare headers
headers = detected_header if detected_header else [f"col_{i+1}" for i in range(len(rows[0]))]
# Convert rows to text chunks
text_content = self._rows_to_text(
rows, headers, max_rows_per_chunk, include_row_numbers
)
# Create base metadata
base_metadata = self.get_base_metadata(file_path)
base_metadata.update({
'encoding': detected_encoding,
'content_type': 'csv',
'delimiter': detected_delimiter,
'quotechar': quotechar,
'has_header': bool(detected_header),
'column_count': len(headers),
'row_count': len(rows),
'headers': headers,
'max_rows_per_chunk': max_rows_per_chunk,
'include_row_numbers': include_row_numbers
})
# Chunk the text content
chunks = self.chunker.chunk_text(text_content, base_metadata)
for i, (chunk_text, chunk_metadata) in enumerate(chunks):
yield DocumentChunk(
content=chunk_text,
metadata=chunk_metadata,
chunk_index=i,
source_file=file_path
)
except Exception as e:
logger.error(f"Error extracting from CSV file {file_path}: {type(e).__name__} - {str(e)}")
raise ValueError(f"Failed to extract CSV content: {str(e)}") from e
async def _read_csv_file(
self,
file_path: Path,
has_header: Optional[bool],
delimiter: Optional[str],
quotechar: str,
encoding: Optional[str]
) -> tuple:
"""
Read and parse CSV file with automatic detection.
Args:
file_path: Path to the CSV file
has_header: Whether file has headers
delimiter: CSV delimiter
quotechar: Quote character
encoding: Text encoding
Returns:
Tuple of (rows, headers, detected_delimiter, detected_encoding)
"""
def _read_sync():
# Auto-detect encoding
detected_encoding = encoding
if detected_encoding is None:
try:
# Try UTF-8 first
with open(file_path, 'r', encoding='utf-8') as f:
sample = f.read(1024)
detected_encoding = 'utf-8'
except UnicodeDecodeError:
# Fallback to other encodings
encodings_to_try = ['utf-16', 'utf-32', 'latin-1', 'cp1252']
for enc in encodings_to_try:
try:
with open(file_path, 'r', encoding=enc) as f:
sample = f.read(1024)
detected_encoding = enc
break
except UnicodeDecodeError:
continue
else:
# Last resort
detected_encoding = 'utf-8'
# Read full file content
with open(file_path, 'r', encoding=detected_encoding, errors='replace') as f:
content = f.read()
# Auto-detect delimiter if not specified
detected_delimiter = delimiter
if detected_delimiter is None:
detected_delimiter = self._detect_delimiter(content)
# Parse CSV
csv_reader = csv.reader(
io.StringIO(content),
delimiter=detected_delimiter,
quotechar=quotechar
)
rows = list(csv_reader)
# Remove empty rows
rows = [row for row in rows if any(cell.strip() for cell in row)]
if not rows:
return [], None, detected_delimiter, detected_encoding
# Auto-detect headers if not specified
detected_header = None
if has_header is None:
# Simple heuristic: if first row contains mostly strings and no numbers,
# assume it's a header
first_row = rows[0]
if len(first_row) > 1: # Need at least 2 columns
non_numeric_count = sum(1 for cell in first_row if not self._is_numeric(cell))
if non_numeric_count >= len(first_row) * 0.7: # 70% non-numeric
detected_header = first_row
rows = rows[1:] # Remove header from data rows
elif has_header:
detected_header = rows[0]
rows = rows[1:]
return rows, detected_header, detected_delimiter, detected_encoding
# Run file reading in thread pool
loop = asyncio.get_event_loop()
return await loop.run_in_executor(None, _read_sync)
def _detect_delimiter(self, content: str) -> str:
"""
Auto-detect CSV delimiter by analyzing sample content.
Args:
content: CSV content sample
Returns:
Detected delimiter character
"""
delimiters = [',', ';', '\t', '|', ':']
# Sample first few lines
lines = content.split('\n')[:5]
if len(lines) < 2:
return ',' # Default fallback
# Count occurrences of each delimiter in each line
delimiter_counts = {}
for delimiter in delimiters:
counts = []
for line in lines:
count = line.count(delimiter)
counts.append(count)
# Use the delimiter that appears consistently across lines
if len(set(counts)) == 1 and counts[0] > 0:
delimiter_counts[delimiter] = counts[0]
# Return delimiter with highest consistent count
if delimiter_counts:
return max(delimiter_counts, key=delimiter_counts.get)
return ',' # Default fallback
def _is_numeric(self, value: str) -> bool:
"""
Check if a string value represents a number.
Args:
value: String value to check
Returns:
True if value appears to be numeric
"""
try:
float(value.replace(',', '').replace(' ', ''))
return True
except ValueError:
return False
def _rows_to_text(
self,
rows: List[List[str]],
headers: List[str],
max_rows_per_chunk: int,
include_row_numbers: bool
) -> str:
"""
Convert CSV rows to formatted text.
Args:
rows: CSV data rows
headers: Column headers
max_rows_per_chunk: Maximum rows per chunk
include_row_numbers: Whether to include row numbers
Returns:
Formatted text representation
"""
if not rows:
return "Empty CSV file\n"
text_parts = []
# Process rows in chunks to avoid memory issues with large files
for i in range(0, len(rows), max_rows_per_chunk):
chunk_rows = rows[i:i + max_rows_per_chunk]
for row_idx, row in enumerate(chunk_rows):
global_row_idx = i + row_idx + 1 # 1-based row numbering
if include_row_numbers:
text_parts.append(f"Row {global_row_idx}:\n")
else:
text_parts.append("Row:\n")
# Ensure row has same number of columns as headers
while len(row) < len(headers):
row.append("")
row = row[:len(headers)] # Truncate if too many columns
# Format each column
for col_idx, (header, value) in enumerate(zip(headers, row)):
text_parts.append(f" {header}: {value}\n")
text_parts.append("\n") # Blank line between rows
return "".join(text_parts)
# Register the CSV loader
def _register_csv_loader():
"""Register CSV loader with the registry."""
try:
from .registry import register_loader
register_loader(CSVLoader, ['csv'])
logger.debug("CSV loader registered successfully")
except ImportError:
logger.debug("Registry not available during import")
# Auto-register when module is imported
_register_csv_loader()
```
--------------------------------------------------------------------------------
/archive/docs-removed-2025-08-23/ubuntu.md:
--------------------------------------------------------------------------------
```markdown
# Ubuntu Setup Guide
This guide provides comprehensive instructions for setting up MCP Memory Service on Ubuntu systems, covering both desktop and server environments.
## Prerequisites
- **Ubuntu 20.04 LTS or later** (22.04 LTS recommended)
- **Python 3.10+** (Python 3.11 recommended)
- **Git** for repository cloning
- **Build essentials** for compiling dependencies
## Quick Installation
### Automatic Installation (Recommended)
```bash
# Update system packages
sudo apt update && sudo apt upgrade -y
# Clone repository
git clone https://github.com/doobidoo/mcp-memory-service.git
cd mcp-memory-service
# Run Ubuntu-specific installer
python install.py --ubuntu
```
The installer automatically:
- Detects Ubuntu version and architecture
- Installs system dependencies
- Configures optimal storage backend
- Sets up CUDA support (if available)
## Manual Installation
### 1. System Dependencies
```bash
# Update package lists
sudo apt update
# Install essential build tools
sudo apt install -y build-essential git curl wget
# Install Python and development headers
sudo apt install -y python3.11 python3.11-dev python3.11-venv python3-pip
# Install additional dependencies
sudo apt install -y libsqlite3-dev libffi-dev libssl-dev
```
### 2. CUDA Support (Optional)
For GPU acceleration on systems with NVIDIA GPUs:
```bash
# Check for NVIDIA GPU
lspci | grep -i nvidia
# Install NVIDIA drivers (if not already installed)
sudo apt install -y nvidia-driver-535
# Install CUDA toolkit
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.0-1_all.deb
sudo dpkg -i cuda-keyring_1.0-1_all.deb
sudo apt update
sudo apt install -y cuda-toolkit-12-1
# Reboot to load drivers
sudo reboot
```
### 3. Python Environment Setup
```bash
# Navigate to project directory
cd mcp-memory-service
# Create virtual environment
python3.11 -m venv venv
source venv/bin/activate
# Upgrade pip
pip install --upgrade pip setuptools wheel
```
### 4. Install Dependencies
#### For CUDA Systems
```bash
# Install PyTorch with CUDA support
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
# Install project dependencies
pip install -e .
pip install chromadb sentence-transformers
```
#### For CPU-only Systems
```bash
# Install CPU-only PyTorch
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
# Install with SQLite-vec backend (recommended for servers)
pip install -e .
pip install sentence-transformers sqlite-vec
```
## Configuration
### Environment Variables
#### For CUDA Systems
```bash
# Add to ~/.bashrc or ~/.profile
export MCP_MEMORY_STORAGE_BACKEND=chromadb
export MCP_MEMORY_USE_CUDA=true
export MCP_MEMORY_CHROMA_PATH="$HOME/.mcp_memory_chroma"
# CUDA-specific settings
export CUDA_VISIBLE_DEVICES=0
export CUDA_LAUNCH_BLOCKING=1
```
#### For CPU-only Systems
```bash
# Add to ~/.bashrc or ~/.profile
export MCP_MEMORY_STORAGE_BACKEND=sqlite_vec
export MCP_MEMORY_SQLITE_VEC_PATH="$HOME/.mcp_memory_sqlite"
export MCP_MEMORY_CPU_ONLY=true
# CPU optimization
export OMP_NUM_THREADS=$(nproc)
export MKL_NUM_THREADS=$(nproc)
```
#### For Server Deployments
```bash
# Server-specific settings
export MCP_MEMORY_HTTP_HOST=0.0.0.0
export MCP_MEMORY_HTTP_PORT=8000
export MCP_MEMORY_LOG_LEVEL=INFO
export MCP_MEMORY_SERVER_MODE=true
```
### Reload Environment
```bash
# Reload shell configuration
source ~/.bashrc
# Or restart shell session
exec bash
```
## Service Integration
### Systemd Service
Create a systemd service for automatic startup:
```bash
# Create service file
sudo tee /etc/systemd/system/mcp-memory.service > /dev/null <<EOF
[Unit]
Description=MCP Memory Service
After=network.target
[Service]
Type=simple
User=$USER
WorkingDirectory=/path/to/mcp-memory-service
ExecStart=/path/to/mcp-memory-service/venv/bin/python src/mcp_memory_service/server.py
Restart=always
RestartSec=10
Environment=MCP_MEMORY_STORAGE_BACKEND=sqlite_vec
Environment=MCP_MEMORY_SERVER_MODE=true
[Install]
WantedBy=multi-user.target
EOF
# Enable and start service
sudo systemctl daemon-reload
sudo systemctl enable mcp-memory.service
sudo systemctl start mcp-memory.service
# Check service status
sudo systemctl status mcp-memory.service
```
### Docker Integration
#### Using Docker Compose
```yaml
# docker-compose.yml
version: '3.8'
services:
mcp-memory-service:
build: .
ports:
- "8000:8000"
environment:
- MCP_MEMORY_STORAGE_BACKEND=sqlite_vec
- MCP_MEMORY_HTTP_HOST=0.0.0.0
- MCP_MEMORY_HTTP_PORT=8000
volumes:
- ./data:/app/data
- /etc/localtime:/etc/localtime:ro
restart: unless-stopped
deploy:
resources:
limits:
cpus: '2.0'
memory: 4G
```
```bash
# Deploy with Docker Compose
docker-compose up -d
# View logs
docker-compose logs -f mcp-memory-service
```
## Claude Desktop Integration on Ubuntu
### Installation
```bash
# Install Claude Desktop (if not already installed)
# Download from https://claude.ai/download or use snap
sudo snap install claude-desktop
```
### Configuration
Claude Desktop configuration location: `~/.config/Claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"memory": {
"command": "python",
"args": ["/path/to/mcp-memory-service/src/mcp_memory_service/server.py"],
"env": {
"MCP_MEMORY_STORAGE_BACKEND": "sqlite_vec",
"PATH": "/path/to/mcp-memory-service/venv/bin:/usr/bin:/bin"
}
}
}
}
```
## VS Code Integration
### Installation
```bash
# Install VS Code
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg
sudo install -o root -g root -m 644 packages.microsoft.gpg /etc/apt/trusted.gpg.d/
sudo sh -c 'echo "deb [arch=amd64,arm64,armhf signed-by=/etc/apt/trusted.gpg.d/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list'
sudo apt update
sudo apt install -y code
```
### MCP Extension Configuration
1. Install the MCP extension in VS Code
2. Configure in VS Code settings:
```json
{
"mcp.servers": {
"memory": {
"command": "python",
"args": ["/path/to/mcp-memory-service/src/mcp_memory_service/server.py"],
"env": {
"MCP_MEMORY_STORAGE_BACKEND": "sqlite_vec"
}
}
}
}
```
## Server Deployment
### Nginx Reverse Proxy
```bash
# Install Nginx
sudo apt install -y nginx
# Create Nginx configuration
sudo tee /etc/nginx/sites-available/mcp-memory > /dev/null <<EOF
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
# SSE support
proxy_buffering off;
proxy_cache off;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
}
}
EOF
# Enable site
sudo ln -s /etc/nginx/sites-available/mcp-memory /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```
### SSL with Let's Encrypt
```bash
# Install Certbot
sudo apt install -y certbot python3-certbot-nginx
# Obtain SSL certificate
sudo certbot --nginx -d your-domain.com
# Auto-renewal is configured automatically
```
### Firewall Configuration
```bash
# Configure UFW firewall
sudo ufw enable
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw allow 8000 # If accessing directly
# Check status
sudo ufw status
```
## Performance Optimization
### CPU Optimization
```bash
# Set CPU governor to performance mode
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
# Install CPU frequency utilities
sudo apt install -y cpufrequtils
sudo cpufreq-set -r -g performance
```
### Memory Optimization
```bash
# Increase shared memory (for large datasets)
echo "kernel.shmmax = 68719476736" | sudo tee -a /etc/sysctl.conf
echo "kernel.shmall = 4294967296" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
```
### SSD Optimization
```bash
# Enable TRIM for SSDs
sudo systemctl enable fstrim.timer
sudo systemctl start fstrim.timer
# Check TRIM status
sudo fstrim -v /
```
## Monitoring and Logging
### System Monitoring
```bash
# Install monitoring tools
sudo apt install -y htop iotop nethogs
# Monitor MCP Memory Service
htop -p $(pgrep -f "mcp_memory_service")
# Monitor GPU usage (if CUDA)
watch -n 1 nvidia-smi
```
### Log Management
```bash
# Configure log rotation
sudo tee /etc/logrotate.d/mcp-memory > /dev/null <<EOF
/path/to/mcp-memory-service/logs/*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
copytruncate
}
EOF
# Test log rotation
sudo logrotate -d /etc/logrotate.d/mcp-memory
```
## Troubleshooting
### Common Ubuntu Issues
#### 1. Python Version Issues
**Symptom**: Wrong Python version or missing python3.11
**Solution**:
```bash
# Add deadsnakes PPA for newer Python versions
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update
sudo apt install -y python3.11 python3.11-dev python3.11-venv
# Set python3.11 as default (optional)
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1
```
#### 2. Permission Issues
**Symptom**: Permission denied errors
**Solution**:
```bash
# Fix file permissions
chmod +x scripts/*.sh
chmod +x scripts/install_ubuntu.sh
# Add user to required groups
sudo usermod -a -G dialout,plugdev $USER
# Re-login or run:
exec bash
```
#### 3. CUDA Driver Issues
**Symptom**: CUDA not detected or driver conflicts
**Solution**:
```bash
# Check CUDA installation
nvidia-smi
nvcc --version
# Reinstall NVIDIA drivers if needed
sudo apt purge nvidia-*
sudo apt autoremove
sudo apt install -y nvidia-driver-535
sudo reboot
# Verify installation
python -c "import torch; print(f'CUDA available: {torch.cuda.is_available()}')"
```
#### 4. SQLite-vec Installation Issues
**Symptom**: SQLite-vec fails to install or import
**Solution**:
```bash
# Install system SQLite development headers
sudo apt install -y libsqlite3-dev
# Reinstall sqlite-vec
pip uninstall -y sqlite-vec
pip install sqlite-vec --no-cache-dir
# Test installation
python -c "import sqlite_vec; print('SQLite-vec installed successfully')"
```
#### 5. Service Startup Issues
**Symptom**: Systemd service fails to start
**Solution**:
```bash
# Check service status and logs
sudo systemctl status mcp-memory.service
sudo journalctl -u mcp-memory.service -f
# Check configuration
sudo systemctl edit mcp-memory.service
# Restart service
sudo systemctl restart mcp-memory.service
```
### Diagnostic Commands
#### System Information
```bash
# Check Ubuntu version
lsb_release -a
# Check system resources
free -h
df -h
lscpu
# Check GPU information
lspci | grep -i vga
nvidia-smi # If NVIDIA GPU present
```
#### Environment Verification
```bash
# Check virtual environment
echo $VIRTUAL_ENV
which python
python --version
# Check installed packages
pip list | grep -E "(torch|sentence|chroma|sqlite)"
# Test imports
python -c "
import torch
import sentence_transformers
print(f'PyTorch: {torch.__version__}')
print(f'CUDA available: {torch.cuda.is_available()}')
"
```
#### Service Health
```bash
# Test server startup
python scripts/verify_environment.py
# Test HTTP server (if configured)
curl http://localhost:8000/health
# Check database access
python -c "
from src.mcp_memory_service.storage.sqlite_vec import SqliteVecStorage
storage = SqliteVecStorage()
print('Database connection: OK')
"
```
## Development Environment
### Setting up for Development
```bash
# Clone repository
git clone https://github.com/doobidoo/mcp-memory-service.git
cd mcp-memory-service
# Create development environment
python3.11 -m venv venv-dev
source venv-dev/bin/activate
# Install in development mode
pip install -e .
pip install pytest black isort mypy pre-commit
# Set up pre-commit hooks
pre-commit install
# Run tests
pytest tests/ -v
```
### Ubuntu-Specific Testing
```bash
# Run Ubuntu-specific tests
pytest tests/platform/test_ubuntu.py -v
# Test CUDA functionality (if available)
pytest tests/cuda/ -v
# Test server deployment
pytest tests/integration/test_server.py -v
```
## Related Documentation
- [Installation Guide](../installation/master-guide.md) - General installation instructions
- [Multi-Client Setup](../integration/multi-client.md) - Multi-client configuration
- [Docker Deployment](../deployment/docker.md) - Docker setup details
- [Troubleshooting](../troubleshooting/general.md) - Ubuntu-specific troubleshooting
- [Performance Tuning](../implementation/performance.md) - Performance optimization
```
--------------------------------------------------------------------------------
/archive/docs-removed-2025-08-23/development/dream-inspired-memory-consolidation.md:
--------------------------------------------------------------------------------
```markdown
# Dream-Inspired Memory Consolidation System
## Overview
This document describes an innovative approach to memory consolidation inspired by human cognitive processes during sleep. Originally conceptualized on June 7, 2025, and contributed to [Issue #11](https://github.com/doobidoo/mcp-memory-service/issues/11) on July 28, 2025, this system enhances the proposed multi-layered consolidation architecture with biologically-inspired mechanisms.
> **📚 Implementation Guide**: For a complete guide on implementing this system autonomously without external AI, see the [Autonomous Memory Consolidation Guide](./autonomous-memory-consolidation.md).
## Background
Traditional memory systems face challenges with:
- Unbounded growth leading to performance degradation
- Difficulty surfacing truly important information
- Lack of creative discovery mechanisms
- No natural "forgetting" process
The dream-inspired approach addresses these by mimicking how human brains consolidate memories during sleep cycles.
## Core Concepts
### 1. Exponential Decay Scoring
Memories naturally lose relevance over time unless reinforced by connections or access patterns.
```python
def calculate_memory_relevance(memory, current_time):
"""
Calculate memory relevance using exponential decay.
Factors:
- Age of memory
- Base importance score
- Retention period (varies by memory type)
"""
age = current_time - memory.created_at
base_score = memory.importance_score
# Different memory types have different decay rates
retention_periods = {
'critical': 365, # Critical memories decay slowly
'reference': 180, # Reference material moderately
'temporary': 7, # Temporary notes decay quickly
'default': 30 # Standard memories
}
retention_period = retention_periods.get(memory.memory_type, 30)
decay_factor = math.exp(-age.days / retention_period)
# Connections boost relevance
connection_boost = 1 + (0.1 * len(memory.connections))
return base_score * decay_factor * connection_boost
```
### 2. Creative Association System
During consolidation, the system randomly pairs memories to discover non-obvious connections, similar to how dreams create unexpected associations.
```python
async def creative_association_phase(memories):
"""
Discover creative connections between seemingly unrelated memories.
The "sweet spot" for interesting discoveries is moderate similarity
(0.3-0.7 range) - not too similar, not completely unrelated.
"""
# Sample random pairs (limit to prevent combinatorial explosion)
max_pairs = min(100, len(memories) * (len(memories) - 1) // 2)
pairs = random.sample(
list(combinations(memories, 2)),
k=min(max_pairs, len(combinations(memories, 2)))
)
associations = []
for mem1, mem2 in pairs:
similarity = calculate_semantic_similarity(mem1, mem2)
# Sweet spot for creative connections
if 0.3 < similarity < 0.7:
# Analyze why they might be connected
connection_reason = analyze_connection(mem1, mem2)
# Create association memory
association = await create_association_memory(
source_memories=[mem1.hash, mem2.hash],
similarity_score=similarity,
connection_type=connection_reason,
metadata={
"discovery_method": "creative_association",
"discovery_date": datetime.now(),
"tags": ["association", "discovery"]
}
)
associations.append(association)
return associations
```
### 3. Controlled Forgetting (Memory Pruning)
Not all memories need to be retained forever. The system implements intelligent forgetting to maintain efficiency.
```python
async def memory_pruning_phase(time_horizon):
"""
Implement controlled forgetting for memory health.
Rather than deleting, we compress and archive low-value memories.
"""
# Get candidates for forgetting
candidates = await get_pruning_candidates(time_horizon)
pruned_memories = []
for memory in candidates:
if should_forget(memory):
# Don't delete - compress and archive
compressed = await compress_memory(memory)
# Create summary if part of a pattern
if memory.cluster_id:
await update_cluster_summary(memory.cluster_id, compressed)
# Archive the original
await archive_memory(memory, compressed)
pruned_memories.append(memory.hash)
return pruned_memories
def should_forget(memory):
"""
Determine if a memory should be forgotten.
Factors:
- Relevance score below threshold
- No recent access
- No important connections
- Not tagged as important
"""
if memory.tags.intersection({'important', 'critical', 'reference'}):
return False
if memory.relevance_score < 0.1:
if not memory.connections:
if (datetime.now() - memory.last_accessed).days > 90:
return True
return False
```
### 4. Semantic Compression
Create condensed representations of memory clusters for efficient long-term storage.
```python
async def semantic_compression(memory_cluster):
"""
Compress a cluster of related memories into a semantic summary.
This creates a higher-level abstraction while preserving key information.
"""
# Extract key concepts using NLP
concepts = extract_key_concepts(memory_cluster)
# Identify recurring themes
themes = identify_themes(concepts)
# Create compressed representation
compressed = {
"summary": generate_thematic_summary(themes),
"key_concepts": concepts[:10], # Top 10 concepts
"temporal_range": {
"start": min(m.created_at for m in memory_cluster),
"end": max(m.created_at for m in memory_cluster)
},
"source_count": len(memory_cluster),
"aggregate_tags": aggregate_tags(memory_cluster),
"embedding": generate_concept_embedding(concepts),
"compression_ratio": calculate_compression_ratio(memory_cluster)
}
# Store as consolidated memory
consolidated = await store_consolidated_memory(
content=compressed["summary"],
metadata={
**compressed,
"type": "semantic_compression",
"compression_date": datetime.now()
}
)
# Link source memories
for memory in memory_cluster:
await add_memory_link(memory.hash, consolidated.hash, "compressed_into")
return consolidated
```
## Autonomous Implementation
**🚀 This entire system can run autonomously without external AI!** The key insight is that the MCP Memory Service already generates embeddings when memories are stored. These embeddings enable:
- Mathematical similarity calculations (cosine similarity)
- Clustering algorithms (DBSCAN, hierarchical clustering)
- Statistical summarization (TF-IDF, centroid method)
- Rule-based decision making
For detailed implementation without AI dependencies, see the [Autonomous Memory Consolidation Guide](./autonomous-memory-consolidation.md).
## Integration with Time-Based Layers
The dream-inspired mechanisms enhance each consolidation layer:
### Daily Processing (Light Touch)
- Calculate initial relevance scores
- Identify highly-connected memory clusters
- Flag memories showing rapid decay
### Weekly Processing (Active Consolidation)
- Run creative association discovery
- Begin semantic compression of stable clusters
- Apply light pruning to obvious temporary content
### Monthly Processing (Deep Integration)
- Comprehensive relevance recalculation
- Controlled forgetting with archival
- Create month-level semantic summaries
### Quarterly Processing (Pattern Extraction)
- Deep creative association analysis
- Major semantic compression operations
- Identify long-term knowledge structures
### Yearly Processing (Knowledge Crystallization)
- Final compression of historical data
- Archive rarely-accessed memories
- Create year-level knowledge maps
## Implementation Architecture
```python
class DreamInspiredConsolidator:
"""
Main consolidation engine with biologically-inspired processing.
"""
def __init__(self, storage, config):
self.storage = storage
self.config = config
self.decay_calculator = ExponentialDecayCalculator(config)
self.association_engine = CreativeAssociationEngine(storage)
self.compression_engine = SemanticCompressionEngine()
self.pruning_engine = ControlledForgettingEngine(storage)
async def consolidate(self, time_horizon: str):
"""
Run full consolidation pipeline for given time horizon.
"""
# 1. Retrieve memories for processing
memories = await self.storage.get_memories_for_horizon(time_horizon)
# 2. Calculate/update relevance scores
await self.update_relevance_scores(memories)
# 3. Cluster by semantic similarity
clusters = await self.cluster_memories(memories)
# 4. Run creative associations (if appropriate for horizon)
if time_horizon in ['weekly', 'monthly']:
associations = await self.association_engine.discover(memories)
await self.storage.store_associations(associations)
# 5. Compress clusters
for cluster in clusters:
if len(cluster) >= self.config.min_cluster_size:
await self.compression_engine.compress(cluster)
# 6. Controlled forgetting (if appropriate)
if time_horizon in ['monthly', 'quarterly', 'yearly']:
await self.pruning_engine.prune(memories)
# 7. Generate consolidation report
return await self.generate_report(time_horizon, memories, clusters)
```
## Configuration Options
```yaml
dream_consolidation:
# Decay settings
decay:
enabled: true
retention_periods:
critical: 365
reference: 180
standard: 30
temporary: 7
# Creative association settings
associations:
enabled: true
min_similarity: 0.3
max_similarity: 0.7
max_pairs_per_run: 100
# Forgetting settings
forgetting:
enabled: true
relevance_threshold: 0.1
access_threshold_days: 90
archive_location: "./memory_archive"
# Compression settings
compression:
enabled: true
min_cluster_size: 5
max_summary_length: 500
preserve_originals: true
```
## Benefits
1. **Natural Scalability**: System automatically manages growth through decay and forgetting
2. **Serendipitous Discovery**: Creative associations reveal unexpected insights
3. **Cognitive Realism**: Mirrors human memory, making it more intuitive
4. **Performance Optimization**: Compression and archival maintain speed
5. **Adaptive Importance**: Memory relevance evolves based on usage and connections
## Safety Considerations
- Never auto-delete memories marked as 'critical' or 'important'
- Always compress before archiving
- Maintain audit trail of all consolidation operations
- Allow users to recover archived memories
- Provide options to disable forgetting for specific memory types
## Future Enhancements
1. **Sleep Cycle Simulation**: Run different consolidation phases at different "depths"
2. **Dream Journal**: Track and analyze creative associations over time
3. **Memory Replay**: Periodically resurface old memories for potential new connections
4. **Adaptive Decay Rates**: Learn optimal retention periods from user behavior
5. **Emotional Tagging**: Consider emotional context in consolidation decisions
## Related Resources
- 📋 [Issue #11: Multi-Layered Memory Consolidation System](https://github.com/doobidoo/mcp-memory-service/issues/11)
- 🤖 [Autonomous Memory Consolidation Guide](./autonomous-memory-consolidation.md) - Complete implementation without external AI
- 📚 [MCP Memory Service Documentation](https://github.com/doobidoo/mcp-memory-service)
## Conclusion
The dream-inspired memory consolidation system brings biological realism to digital memory management. By implementing natural processes like decay, creative association, and controlled forgetting, we create a system that not only scales efficiently but also surfaces truly meaningful information while discovering unexpected connections.
This approach transforms memory management from a storage problem into a living, breathing system that evolves with its user's needs.
---
*Original concept: June 7, 2025*
*Contributed to Issue #11: July 28, 2025*
*Documentation created: July 28, 2025*
```
--------------------------------------------------------------------------------
/archive/docs-removed-2025-08-23/invocation_guide.md:
--------------------------------------------------------------------------------
```markdown
# MCP Memory Service Invocation Guide
This document outlines the specific keywords and phrases that trigger the MCP memory service in Claude Desktop conversations, along with examples of how to use them effectively.
## Memory Storage Keywords
The following keywords and phrases will trigger Claude to store information in the MCP memory service:
| Keyword/Phrase | Example Usage | Notes |
|----------------|---------------|-------|
| "remember that" | "Please remember that my project deadline is May 15th." | Most common and reliable trigger |
| "remember this" | "Remember this: The database credentials are in the config file." | Works well for specific information |
| "save to memory" | "Save to memory that I prefer dark mode in all applications." | Explicit storage command |
| "store in memory" | "Store in memory that my team meeting is every Tuesday." | Explicit storage command |
| "add to memory" | "Add to memory that the client prefers email communication." | Works for appending information |
| "make a note" | "Make a note that we need to follow up next week." | More conversational trigger |
| "remember for later" | "Remember for later that we discussed the API integration approach." | Implies future retrieval |
| "keep track of" | "Keep track of the fact that version 2.3 has this bug." | Good for tracking issues |
## Memory Retrieval Keywords
The following keywords and phrases will trigger Claude to retrieve information from the MCP memory service:
| Keyword/Phrase | Example Usage | Notes |
|----------------|---------------|-------|
| "do you remember" | "Do you remember my favorite color?" | Natural question format |
| "recall" | "Recall what we discussed about the database schema." | Direct command for retrieval |
| "what did I tell you about" | "What did I tell you about my project timeline?" | Conversational retrieval |
| "retrieve from memory" | "Retrieve from memory our discussion about authentication." | Explicit retrieval command |
| "search your memory for" | "Search your memory for information about the API design." | Useful for specific searches |
| "find in your memory" | "Find in your memory our discussion from last week." | Good for time-based retrieval |
| "check your memory for" | "Check your memory for the requirements we outlined." | Alternative retrieval phrase |
| "what do you know about" | "What do you know about my team structure?" | Natural question format |
### How Retrieved Information Works in Conversation
When Claude retrieves information from the MCP memory service, it's important to understand how this affects the current conversation:
1. **Automatic Context Integration**: When memories are retrieved, they are automatically included in Claude's context for the current conversation. This means Claude will immediately incorporate and reference this information in its responses.
2. **Trial and Error Retrieval**: If your first retrieval attempt doesn't find the information you're looking for, Claude's responses about "no matching memories" do NOT get stored in the memory service. You can safely make multiple retrieval attempts without cluttering the memory database.
3. **Selective Memory Storage**: Just because information was retrieved doesn't mean the current conversation about that retrieval is automatically stored. The current conversation will only be stored if you explicitly ask Claude to remember it using one of the memory storage keywords.
### Multi-Step Retrieval Workflow
A common workflow for effectively using the memory service:
1. **Initial Retrieval**: "Search your memory for information about the marketing campaign we discussed."
2. **Refinement**: If the first search isn't what you need, you can refine it: "Try searching for memories with the tag 'Q3 marketing' instead."
3. **Further Refinement**: "Check if there's anything from conversations we had in March about social media strategy."
4. **Found Relevant Information**: Once Claude retrieves the information you want, you can continue your conversation based on this information.
5. **Explicit Storage**: If you want the current conversation to be remembered: "Please remember this conversation about our refined marketing approach with tags 'marketing strategy' and 'revised'."
This approach lets you search through past memories with multiple attempts until you find what you need, without storing all the search attempts, and then explicitly store only the valuable parts of the current conversation.
### Behind-the-Scenes Example: Retrieving Mistral Model Information
Let's walk through a complete example showing what happens behind the scenes when retrieving and using memory:
**Current Conversation Context**: Human and Claude are discussing coreference resolution using FastCoref and Ollama Mistral.
#### Step 1: Initial Retrieval Attempt
**Human**: "Do you remember which Mistral model I said I was planning to use?"
**Behind the Scenes**:
- Claude recognizes the phrase "Do you remember" as a memory retrieval trigger
- Claude calls the MCP memory service using the `retrieve_memory` function:
```
retrieve_memory({"query": "Mistral model user plans to use", "n_results": 5})
```
- MCP searches the ChromaDB vector database using the query embedding
- MCP finds a few memories about Mistral but not the specific model information
- MCP returns these results to Claude
**Claude**: "I found some information about Mistral in my memory, but I don't see specifics about which model you were planning to use. Would you like me to share what I found about Mistral in general?"
#### Step 2: Refined Retrieval Attempt
**Human**: "Try checking memories with the tag 'models' or anything from our conversation last Tuesday."
**Behind the Scenes**:
- Claude recognizes this as a refined memory retrieval request
- Claude makes two MCP function calls:
```
search_by_tag({"tags": ["models"]})
```
and
```
recall_memory({"query": "conversation from last Tuesday about Mistral", "n_results": 5})
```
- MCP processes both requests, checking for tag matches and time-based retrieval
- MCP still doesn't find the exact information and returns partial matches
**Claude**: "I found some memories tagged 'models' but nothing specific about which Mistral model you plan to use. I did find a conversation from last Tuesday, but it was about a different topic."
#### Step 3: More Specific Retrieval
**Human**: "Search your memory for anything I mentioned about 'Mistral-7B' specifically."
**Behind the Scenes**:
- Claude calls MCP again with a more specific query:
```
retrieve_memory({"query": "Mistral-7B model mentioned by user", "n_results": 5})
```
- MCP searches with this specific term
- MCP finds a relevant memory about the user planning to use Mistral-7B-Instruct
- MCP returns this memory to Claude
**Claude**: "I found it! In a previous conversation, you mentioned that you planned to use the Mistral-7B-Instruct model with Ollama because it has good performance for coreference resolution while being efficient enough to run on your local hardware."
#### Step 4: Continuing the Productive Conversation
**Human**: "Great! So based on that, how should I configure FastCoref to work optimally with Mistral-7B-Instruct via Ollama?"
**Behind the Scenes**:
- Claude has now incorporated the retrieved memory into its current conversation context
- No additional MCP calls are made at this point
- Claude uses both the retrieved information and its general knowledge to formulate a response
**Claude**: [Provides detailed information about FastCoref configuration with Mistral-7B-Instruct]
#### Step 5: Explicitly Saving the Valuable Conversation
**Human**: "This was very helpful. Please remember this conversation about FastCoref configuration with Mistral-7B-Instruct and tag it as 'coreference', 'configuration', and 'mistral'."
**Behind the Scenes**:
- Claude recognizes "Please remember" as a memory storage trigger
- Claude calls the MCP memory storage function:
```
store_memory({
"content": "Detailed conversation about configuring FastCoref with Mistral-7B-Instruct via Ollama, including settings for optimal coreference resolution.",
"metadata": {
"tags": "coreference,configuration,mistral",
"type": "technical_discussion"
}
})
```
- MCP stores this information in ChromaDB with proper embeddings and metadata
- MCP confirms successful storage to Claude
**Claude**: "I've saved our conversation about FastCoref configuration with Mistral-7B-Instruct and tagged it as requested. You can retrieve this information in future conversations."
This example illustrates how multiple retrieval attempts can be made without cluttering memory, and how only the valuable parts of the conversation are stored when explicitly requested. The MCP service handles the actual storage and retrieval while Claude manages the conversation flow and context integration.
## Tag-Based Retrieval
To retrieve memories based on specific tags, use these phrases:
| Keyword/Phrase | Example Usage | Notes |
|----------------|---------------|-------|
| "find memories with tag" | "Find memories with tag 'project'." | Explicit tag retrieval |
| "search for tag" | "Search for tag 'meeting notes'." | Simple tag search |
| "retrieve memories tagged" | "Retrieve memories tagged 'important'." | Alternative tag phrase |
| "recall information tagged" | "Recall information tagged 'deadline'." | Combines recall with tags |
## Time-Based Retrieval
For retrieving memories based on when they were stored, use these time expressions:
| Time Expression | Example Usage | Notes |
|----------------|---------------|-------|
| "yesterday" | "What did we discuss yesterday?" | Simple day reference |
| "last week" | "Recall our conversation from last week." | Weekly timeframe |
| "two days ago" | "What did I tell you two days ago?" | Specific day count |
| "this morning" | "What did we talk about this morning?" | Time of day reference |
| "last month" | "Find our discussions from last month about the project." | Monthly timeframe |
| "earlier today" | "What did I ask you to remember earlier today?" | Same-day reference |
## Memory Deletion Keywords
To delete specific memories or all memories with certain tags:
| Keyword/Phrase | Example Usage | Notes |
|----------------|---------------|-------|
| "forget" | "Please forget what I told you about my address." | Used for specific deletion |
| "delete from memory" | "Delete from memory our discussion about the old project." | Explicit deletion |
| "remove from memory" | "Remove from memory everything with tag 'temporary'." | Good for tag-based cleanup |
| "clear memories about" | "Clear memories about the prototype discussion." | Content-based deletion |
## Best Practices
1. **Be explicit**: When storing important information, use clear commands like "Please remember that..." rather than assuming Claude will store it automatically.
2. **Use tags when storing**: Add tags to organize memories: "Remember that my favorite color is blue, tag this as 'preferences'."
3. **Be specific when retrieving**: The more specific your retrieval request, the more relevant the results will be.
4. **Combine approaches**: For complex retrievals, combine techniques: "Find memories from last week tagged 'project' about the database design."
5. **Confirm storage**: After asking Claude to remember something important, ask it to confirm what it stored to verify it was captured correctly.
## How It Works Behind the Scenes
When you use these keywords, Claude identifies them as memory-related commands and calls the appropriate MCP memory service functions:
1. For storage, Claude calls `store_memory` with your content and optional metadata
2. For retrieval, Claude calls functions like `retrieve_memory`, `search_by_tag`, or `recall_memory`
3. For deletion, Claude calls `delete_memory` or `delete_by_tag`
The MCP memory service stores this information in a vector database (ChromaDB) that allows for semantic searching, so you can retrieve information even if you don't use the exact same wording as when you stored it.
## Limitations
- The system works best with explicit commands rather than implicit expectations
- Very long or complex information may be summarized when stored
- Memory retrieval is based on relevance scoring, so results might not always include everything on a topic
- Time-based expressions need to be clear (e.g., "last week" works better than "recently")
## Example Workflow
1. **Store information**: "Please remember that our client meeting is scheduled for Thursday at 2 PM, tag this as 'meetings' and 'client'."
2. **Verify storage**: "What do you know about our upcoming client meeting?"
3. **Later retrieval**: "Search your memory for information tagged 'client' from this week."
4. **Clean up outdated information**: "Delete from memory information about client meetings that happened more than a month ago."
```
--------------------------------------------------------------------------------
/.claude/agents/amp-bridge.md:
--------------------------------------------------------------------------------
```markdown
---
name: amp-bridge
description: Direct Amp CLI automation agent for quick refactorings, bug fixes, and complex coding tasks. Uses amp --execute mode for non-interactive automation. Leverages Amp's full toolset (edit_file, create_file, Bash, finder, librarian, oracle) for fast, high-quality code changes without consuming Claude Code credits.
Examples:
- "Use Amp to refactor this function with better type hints and error handling"
- "Ask Amp to fix the bug in generate_tests.sh where base_name is undefined"
- "Have Amp analyze and optimize the storage backend architecture"
- "Use Amp oracle to review the PR automation workflow and suggest improvements"
model: sonnet
color: blue
---
You are the Amp CLI Bridge Agent, a specialized automation agent that leverages Amp CLI's **full coding capabilities** through direct execution mode. Your role is to execute complex refactorings, bug fixes, and code improvements using Amp's powerful toolset while conserving Claude Code credits.
## Core Mission
Execute **fast, high-quality code changes** using Amp CLI's automation capabilities:
- **Quick refactorings** (1-2 minutes) - Type hints, error handling, code style
- **Bug fixes** (2-5 minutes) - Undefined variables, logic errors, edge cases
- **Complex tasks** (5-15 minutes) - Multi-file refactorings, architecture improvements
- **Code review** (using Oracle) - AI-powered planning and expert guidance
## Amp CLI Capabilities
### Available Tools
- `edit_file` - Make precise edits to text files (like Claude Code's Edit tool)
- `create_file` - Create new files
- `Bash` - Execute shell commands
- `finder` - Intelligent codebase search (better than grep for understanding)
- `Grep` - Fast keyword search
- `glob` - File pattern matching
- `librarian` - Specialized codebase understanding agent (for architecture analysis)
- `oracle` - GPT-5 (hypothetical future model) reasoning model for planning, review, expert guidance
- `Task` - Sub-agents for complex multi-step workflows
- `undo_edit` - Roll back changes
- `read_thread` - Access previous Amp thread results
### Execution Modes
**1. Execute Mode** (`--execute` or `-x`)
- Non-interactive, for automation
- Reads prompt from stdin or argument
- Outputs results to stdout
- Perfect for quick tasks
**2. Dangerous All Mode** (`--dangerously-allow-all`)
- Skips all confirmation prompts
- Fully automated execution
- Use for trusted refactorings/fixes
- **WARNING**: Only use when confident in prompt safety
**3. Thread Mode** (`amp threads continue <id>`)
- Continue previous conversations
- Maintain context across complex tasks
- Fork threads for experimentation
### Output Format
**Stream JSON** (`--stream-json`)
- Compatible with Claude Code
- Structured output parsing
- Real-time progress updates
## Operational Workflow
### 1. Quick Refactoring (1-2 minutes)
**Scenario:** Simple, focused code improvements
```bash
# Example: Add type hints to a function
echo "Refactor this Python function to add type hints and improve error handling:
File: scripts/pr/generate_tests.sh
Issue: Missing base_name variable definition
Add this line after line 49:
base_name=\$(basename \"\$file\" .py)
Test the fix and ensure no syntax errors." | \
amp --execute --dangerously-allow-all --no-notifications
```
**When to use:**
- Type hint additions
- Error handling improvements
- Variable renaming
- Code style fixes
- Simple bug fixes (undefined variables, off-by-one errors)
**Time**: <2 minutes
**Cost**: Low credits (simple prompts)
### 2. Bug Fix (2-5 minutes)
**Scenario:** Analyze → Diagnose → Fix workflow
```bash
# Example: Fix undefined variable bug
cat > /tmp/amp_bugfix.txt << 'EOF'
Analyze and fix the undefined variable bug in scripts/pr/generate_tests.sh:
1. Use finder to locate where base_name is used
2. Identify where it should be defined
3. Add the definition with proper quoting
4. Verify the fix doesn't break existing code
Run the fixed script with --help to ensure it works.
EOF
amp --execute --dangerously-allow-all < /tmp/amp_bugfix.txt
```
**When to use:**
- Logic errors requiring analysis
- Edge case handling
- Error propagation issues
- Integration bugs
**Time**: 2-5 minutes
**Cost**: Medium credits (analysis + fix)
### 3. Complex Refactoring (5-15 minutes)
**Scenario:** Multi-file, multi-step improvements
```bash
# Example: Refactor storage backend architecture
amp threads new --execute << 'EOF'
Analyze the storage backend architecture and improve it:
1. Use librarian to understand src/mcp_memory_service/storage/
2. Identify code duplication and abstraction opportunities
3. Propose refactoring plan (don't execute yet, just plan)
4. Get oracle review of the plan
5. If oracle approves, execute refactoring
Focus on:
- DRY violations
- Abstract base class improvements
- Error handling consistency
- Type safety
EOF
```
**When to use:**
- Multi-file refactorings
- Architecture improvements
- Large-scale code reorganization
- Breaking down complex functions
**Time**: 5-15 minutes
**Cost**: High credits (multiple tools, analysis, execution)
### 4. Code Review with Oracle (1-3 minutes)
**Scenario:** Expert AI review before making changes
```bash
# Example: Review PR automation workflow
echo "Review the PR automation workflow in .claude/agents/gemini-pr-automator.md:
Focus on:
1. Workflow logic and edge cases
2. Error handling and retry logic
3. Security considerations (command injection, etc.)
4. Performance optimization opportunities
Provide actionable suggestions ranked by impact." | \
amp --execute --no-notifications
```
**When to use:**
- Pre-implementation planning
- Design reviews
- Security audits
- Performance analysis
**Time**: 1-3 minutes
**Cost**: Medium credits (oracle model)
## Decision Matrix: When to Use Amp vs Claude Code
| Task Type | Use Amp If... | Use Claude Code If... |
|-----------|---------------|----------------------|
| **Quick Refactoring** | Simple, well-defined scope (<10 lines) | Complex logic requiring context |
| **Bug Fix** | Clear bug, known fix pattern | Unclear root cause, needs investigation |
| **Multi-file Refactoring** | Changes follow clear pattern | Requires deep architectural decisions |
| **Code Review** | Need external perspective (oracle) | Part of active development flow |
| **Research** | Web search, external docs | Project-specific context needed |
| **Architecture Analysis** | Fresh codebase perspective (librarian) | Ongoing design decisions |
**Credit Conservation Strategy:**
- **Amp**: External research, independent refactorings, code review
- **Claude Code**: Interactive development, context-heavy decisions, user collaboration
## Prompt Engineering for Amp
### ✅ Effective Prompts
**Concise and actionable:**
```
"Refactor generate_tests.sh to fix undefined base_name variable. Add definition after line 49 with proper quoting."
```
**Structured multi-step:**
```
"1. Use finder to locate all uses of $(cat $file) in scripts/
2. Quote each occurrence as $(cat \"$file\")
3. Test one script to verify fix
4. Apply to all matches"
```
**With safety checks:**
```
"Refactor complexity scoring logic in pre-commit hook.
IMPORTANT: Test with scripts/hooks/pre-commit --help before finishing.
Roll back if tests fail."
```
### ❌ Ineffective Prompts
**Too vague:**
```
"Make the code better" // What code? Which aspects?
```
**Over-specified:**
```
"Add type hints and docstrings and error handling and logging and tests and documentation and..." // Split into focused tasks
```
**Missing context:**
```
"Fix the bug" // Which bug? Which file?
```
## Error Handling
### Insufficient Credits
```bash
# Check credits before expensive tasks
if amp --execute "echo 'credit check'" 2>&1 | grep -q "Insufficient credit"; then
echo "⚠️ Amp credits low. Use simpler prompts or wait for refresh."
exit 1
fi
```
### Execution Failures
```bash
# Always check exit code
if ! amp --execute < prompt.txt; then
echo "❌ Amp execution failed. Check logs: ~/.cache/amp/logs/cli.log"
exit 1
fi
```
### Dangerous Changes
```bash
# For risky refactorings, don't use --dangerously-allow-all
# Let user review changes before applying
echo "Refactor storage backend..." | amp --execute # User will confirm
```
## Integration with Claude Code
### Handoff Pattern
When to hand off TO Amp:
```markdown
User: "This function is too complex, can you simplify it?"
Claude: "This is a good candidate for Amp automation - it's a focused refactoring task.
Let me use the amp-bridge agent to:
1. Analyze the function complexity
2. Break it into smaller functions
3. Add type hints and error handling
4. Test the refactored version
This will take ~2-3 minutes and conserve Claude Code credits."
```
When to take BACK from Amp:
```markdown
Amp completes refactoring → Claude:
"Amp has refactored the function into 3 smaller functions with type hints.
Let me review the changes:
[Shows diff]
The refactoring looks good! Would you like me to:
1. Add comprehensive tests for the new functions
2. Update the documentation
3. Check for any edge cases Amp might have missed?"
```
## Common Use Cases
### 1. Type Hint Addition
```bash
echo "Add complete type hints to src/mcp_memory_service/storage/hybrid.py:
- Function parameters
- Return types
- Class attributes
- Use typing module (List, Dict, Optional, etc.)
Preserve all existing logic." | \
amp --execute --dangerously-allow-all
```
### 2. Error Handling Improvement
```bash
cat << 'EOF' | amp --execute
Improve error handling in scripts/pr/auto_review.sh:
1. Add set -euo pipefail at top
2. Check for required commands (gh, gemini, jq)
3. Add error messages for missing dependencies
4. Handle network failures gracefully
5. Add cleanup on script failure (trap)
Test with --help flag.
EOF
```
### 3. Shell Script Security
```bash
echo "Security audit scripts/pr/quality_gate.sh:
1. Quote all variable expansions
2. Use read -r for input
3. Validate user input
4. Use mktemp for temp files
5. Check command injection risks
Fix any issues found." | amp --execute
```
### 4. Code Deduplication
```bash
cat << 'EOF' | amp --execute
Analyze scripts/pr/ directory for duplicated code:
1. Use finder to identify similar functions across files
2. Extract common code into shared utility (scripts/pr/common.sh)
3. Update all scripts to source the utility
4. Test auto_review.sh and watch_reviews.sh to verify
Don't break existing functionality.
EOF
```
### 5. Architecture Review (Oracle)
```bash
echo "Oracle: Review the PR automation architecture in .claude/agents/:
- gemini-pr-automator.md
- code-quality-guard.md
Assess:
1. Workflow efficiency
2. Error handling robustness
3. Scalability to more review tools (not just Gemini)
4. Security considerations
Provide ranked improvement suggestions." | amp --execute
```
## Success Metrics
- ✅ **Speed**: Refactorings complete in <5 minutes
- ✅ **Quality**: Amp-generated code passes pre-commit hooks
- ✅ **Credit Efficiency**: Amp conserves Claude Code credits for interactive work
- ✅ **Error Rate**: <10% of Amp tasks require manual fixes
- ✅ **User Satisfaction**: Seamless handoff between Amp and Claude Code
## Advanced Patterns
### Thread Continuation for Complex Tasks
```bash
# Start a complex task
AMP_THREAD=$(amp threads new --execute "Analyze storage backend architecture" | grep -oP 'Thread: \K\w+')
# Continue with next step
amp threads continue $AMP_THREAD --execute "Based on analysis, propose refactoring plan"
# Review with oracle
amp threads continue $AMP_THREAD --execute "Oracle: Review the refactoring plan for risks"
# Execute if approved
amp threads continue $AMP_THREAD --execute "Execute approved refactorings"
```
### Parallel Amp Tasks
```bash
# Launch multiple Amp tasks in parallel (careful with credits!)
amp --execute "Refactor file1.py" > /tmp/amp1.log 2>&1 &
amp --execute "Refactor file2.py" > /tmp/amp2.log 2>&1 &
amp --execute "Refactor file3.py" > /tmp/amp3.log 2>&1 &
wait # Wait for all to complete
# Aggregate results
cat /tmp/amp{1,2,3}.log
```
### Amp + Groq Hybrid
```bash
# Use Groq for fast analysis, Amp for execution
complexity=$(./scripts/utils/groq "Rate complexity 1-10: $(cat file.py)")
if [ "$complexity" -gt 7 ]; then
echo "Refactor high-complexity file.py to split into smaller functions" | amp --execute
fi
```
## Communication Style
**User-Facing:**
- "Using Amp to refactor this function - will take ~2 minutes"
- "Amp is analyzing the codebase architecture..."
- "Completed! Amp made 5 improvements across 3 files"
**Progress Updates:**
- "Amp working... (30s elapsed)"
- "Amp oracle reviewing changes..."
- "Amp tests passed ✓"
**Results:**
- "Amp Refactoring Results:"
- Show diff/summary
- Explain changes made
- Note any issues/limitations
Your goal is to make Amp CLI a **powerful coding assistant** that handles focused refactorings, bug fixes, and architecture improvements **quickly and efficiently**, while Claude Code focuses on **interactive development and user collaboration**.
```
--------------------------------------------------------------------------------
/archive/docs-removed-2025-08-23/authentication.md:
--------------------------------------------------------------------------------
```markdown
# MCP Memory Service Authentication Guide
This guide provides comprehensive information about API key authentication in MCP Memory Service, including setup, configuration, security best practices, and troubleshooting.
## Overview
MCP Memory Service supports optional API key authentication for HTTP-based deployments. When enabled, all HTTP API requests must include a valid API key in the Authorization header. This provides security for multi-client deployments and prevents unauthorized access to your memory data.
## API Key Configuration
### Environment Variable
API key authentication is controlled by the `MCP_API_KEY` environment variable:
```bash
# Set API key (enables authentication)
export MCP_API_KEY="your-secure-api-key-here"
# Unset or empty (disables authentication)
unset MCP_API_KEY
```
**Important**: When `MCP_API_KEY` is not set or empty, the HTTP API runs without authentication. This is suitable for local development but **not recommended for production**.
### Generating Secure API Keys
#### Recommended Methods
**1. OpenSSL (recommended)**
```bash
# Generate a 32-byte base64 encoded key
openssl rand -base64 32
# Generate a 32-byte hex encoded key
openssl rand -hex 32
# Generate with specific length
openssl rand -base64 48 # 48-byte key for extra security
```
**2. Python**
```python
import secrets
import base64
# Generate secure random key
key = secrets.token_urlsafe(32)
print(f"MCP_API_KEY={key}")
```
**3. Node.js**
```javascript
const crypto = require('crypto');
// Generate secure random key
const key = crypto.randomBytes(32).toString('base64');
console.log(`MCP_API_KEY=${key}`);
```
#### Key Requirements
- **Minimum length**: 16 characters (32+ recommended)
- **Character set**: Use URL-safe characters (base64/hex encoding recommended)
- **Randomness**: Use cryptographically secure random generation
- **Uniqueness**: Different keys for different environments/deployments
## Service Installation with API Keys
### During Installation
The service installer automatically generates a secure API key:
```bash
# Install with automatic API key generation
python install_service.py
# The installer will display your generated API key
# Example output:
# ✅ API Key Generated: mcp-a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
```
### Finding Your Service API Key
After installation, you can find your API key in several ways:
**1. Service Status Command**
```bash
python install_service.py --status
```
**2. Configuration File**
```bash
# Linux/macOS
cat ~/.mcp_memory_service/service_config.json
# Windows
type %USERPROFILE%\.mcp_memory_service\service_config.json
```
**3. Service Definition File**
```bash
# Linux (systemd)
cat /etc/systemd/system/mcp-memory.service
# or
cat ~/.config/systemd/user/mcp-memory.service
# macOS (LaunchAgent)
cat ~/Library/LaunchAgents/com.mcp.memory-service.plist
# Windows (check service configuration)
sc qc MCPMemoryService
```
## Client Configuration
### Claude Desktop
Configure Claude Desktop to use API key authentication:
```json
{
"mcpServers": {
"memory": {
"command": "node",
"args": ["/path/to/mcp-memory-service/examples/http-mcp-bridge.js"],
"env": {
"MCP_MEMORY_HTTP_ENDPOINT": "https://your-server:8000/api",
"MCP_MEMORY_API_KEY": "your-actual-api-key-here",
"MCP_MEMORY_AUTO_DISCOVER": "false"
}
}
}
}
```
### Web Applications
**JavaScript/TypeScript Example:**
```javascript
class MCPMemoryClient {
constructor(endpoint, apiKey) {
this.endpoint = endpoint;
this.apiKey = apiKey;
}
async storeMemory(content, tags = []) {
const response = await fetch(`${this.endpoint}/memories`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ content, tags })
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
}
async retrieveMemories(query) {
const response = await fetch(`${this.endpoint}/memories/search`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ query })
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
}
}
// Usage
const client = new MCPMemoryClient(
'https://memory.local:8000/api',
process.env.MCP_API_KEY
);
```
### cURL Examples
**Store Memory:**
```bash
curl -X POST https://memory.local:8000/api/memories \
-H "Authorization: Bearer $MCP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Important project decision",
"tags": ["project", "decision"],
"memory_type": "note"
}'
```
**Retrieve Memories:**
```bash
curl -X POST https://memory.local:8000/api/memories/search \
-H "Authorization: Bearer $MCP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "project decisions",
"limit": 10
}'
```
**Health Check:**
```bash
curl -H "Authorization: Bearer $MCP_API_KEY" \
https://memory.local:8000/api/health
```
## Security Best Practices
### Key Management
1. **Environment Variables**: Always store API keys in environment variables, never in code
2. **Separate Keys**: Use different API keys for different environments (dev/staging/prod)
3. **Regular Rotation**: Rotate API keys regularly, especially after team changes
4. **Secure Storage**: Use secrets management systems for production deployments
### Access Control
1. **Principle of Least Privilege**: Limit API key access to necessary personnel only
2. **Network Security**: Combine with network-level security (VPN, firewall rules)
3. **HTTPS Only**: Always use HTTPS in production to encrypt API key transmission
4. **Monitoring**: Log authentication failures (but never log the keys themselves)
### Key Distribution
**DO:**
- Use secure channels for key distribution (encrypted messaging, secrets management)
- Store keys in secure configuration management systems
- Use different keys for different services/environments
- Document key ownership and rotation procedures
**DON'T:**
- Share keys via email, chat, or version control
- Use the same key across multiple environments
- Store keys in plain text files or databases
- Include keys in error messages or logs
## Updating API Keys
### For Service Installations
1. **Stop the service:**
```bash
python install_service.py --stop
```
2. **Generate new key:**
```bash
NEW_API_KEY=$(openssl rand -base64 32)
echo "New API Key: $NEW_API_KEY"
```
3. **Update service configuration:**
```bash
# Linux (edit systemd service file)
sudo nano /etc/systemd/system/mcp-memory.service
# Find: Environment=MCP_API_KEY=old-key-here
# Replace with: Environment=MCP_API_KEY=new-key-here
# Reload systemd
sudo systemctl daemon-reload
```
4. **Update client configurations:**
- Update Claude Desktop config
- Update application environment variables
- Update any scripts or automation
5. **Restart the service:**
```bash
python install_service.py --start
```
### For Manual Deployments
1. **Update environment variable:**
```bash
export MCP_API_KEY="new-secure-api-key-here"
```
2. **Restart the server:**
```bash
# Stop current server (Ctrl+C or kill process)
# Start new server
python scripts/run_http_server.py
```
3. **Test with new key:**
```bash
curl -H "Authorization: Bearer $MCP_API_KEY" \
http://localhost:8000/api/health
```
## Troubleshooting
### Common Authentication Errors
#### 401 Unauthorized
**Symptoms:**
```json
{
"error": "Unauthorized",
"message": "Missing or invalid API key"
}
```
**Causes & Solutions:**
1. **Missing Authorization header**
```bash
# Wrong: No auth header
curl http://localhost:8000/api/memories
# Correct: Include auth header
curl -H "Authorization: Bearer $MCP_API_KEY" http://localhost:8000/api/memories
```
2. **Incorrect header format**
```bash
# Wrong: Missing "Bearer " prefix
curl -H "Authorization: $MCP_API_KEY" http://localhost:8000/api/memories
# Correct: Include "Bearer " prefix
curl -H "Authorization: Bearer $MCP_API_KEY" http://localhost:8000/api/memories
```
3. **Wrong API key**
```bash
# Check server logs for authentication failures
# Verify API key matches server configuration
```
#### 403 Forbidden
**Symptoms:**
```json
{
"error": "Forbidden",
"message": "Invalid API key"
}
```
**Solutions:**
1. Verify the API key matches the server configuration
2. Check for whitespace or encoding issues in the key
3. Ensure the key hasn't been rotated on the server
#### Connection Refused / Network Errors
**Check server status:**
```bash
# Verify server is running
curl -v http://localhost:8000/api/health
# Check service status
python install_service.py --status
# Check server logs
journalctl -u mcp-memory -f # Linux
tail -f ~/.mcp_memory_service/logs/mcp-memory-service.log # Service installation
```
### Debugging Tools
#### Test API Key
Create a simple test script:
```bash
#!/bin/bash
# test-api-key.sh
API_KEY="${MCP_API_KEY:-your-test-key-here}"
ENDPOINT="${MCP_ENDPOINT:-http://localhost:8000/api}"
echo "Testing API key authentication..."
echo "Endpoint: $ENDPOINT"
echo "API Key: ${API_KEY:0:8}..." # Show only first 8 chars
# Test health endpoint
response=$(curl -s -w "\n%{http_code}" \
-H "Authorization: Bearer $API_KEY" \
"$ENDPOINT/health")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" = "200" ]; then
echo "✅ Authentication successful"
echo "Response: $body"
else
echo "❌ Authentication failed"
echo "HTTP Code: $http_code"
echo "Response: $body"
fi
```
#### Server-Side Debugging
Enable debug logging in the server:
```bash
# Set debug logging
export LOG_LEVEL=DEBUG
# Run server with debug output
python scripts/run_http_server.py
```
Debug logs will show:
- Incoming request headers
- Authentication attempts
- API key validation results
## API Key Rotation Strategy
### Recommended Rotation Schedule
- **Development**: As needed (after security incidents)
- **Staging**: Monthly
- **Production**: Quarterly (or after team changes)
### Zero-Downtime Rotation
For production systems requiring zero downtime:
1. **Multiple Keys**: Implement support for multiple valid keys
2. **Staged Rollout**:
- Add new key to server (both keys valid)
- Update all clients to use new key
- Remove old key from server
3. **Monitoring**: Watch for authentication failures during transition
### Emergency Rotation
In case of key compromise:
1. **Immediate**: Rotate the compromised key
2. **Audit**: Check access logs for unauthorized usage
3. **Notify**: Inform relevant team members
4. **Review**: Update rotation procedures as needed
## Integration with Secrets Management
### HashiCorp Vault
```bash
# Store API key in Vault
vault kv put secret/mcp-memory api_key="$(openssl rand -base64 32)"
# Retrieve API key
export MCP_API_KEY=$(vault kv get -field=api_key secret/mcp-memory)
```
### AWS Secrets Manager
```bash
# Store API key
aws secretsmanager create-secret \
--name "mcp-memory/api-key" \
--secret-string "$(openssl rand -base64 32)"
# Retrieve API key
export MCP_API_KEY=$(aws secretsmanager get-secret-value \
--secret-id "mcp-memory/api-key" \
--query 'SecretString' --output text)
```
### Azure Key Vault
```bash
# Store API key
az keyvault secret set \
--vault-name "my-vault" \
--name "mcp-memory-api-key" \
--value "$(openssl rand -base64 32)"
# Retrieve API key
export MCP_API_KEY=$(az keyvault secret show \
--vault-name "my-vault" \
--name "mcp-memory-api-key" \
--query 'value' -o tsv)
```
## Compliance Considerations
### Data Protection
- API keys provide access to potentially sensitive memory data
- Implement appropriate data classification and handling procedures
- Consider encryption at rest for stored memories
- Maintain audit logs of API access
### Regulatory Requirements
For organizations subject to compliance requirements:
1. **Key Lifecycle Management**: Document key generation, distribution, rotation, and revocation
2. **Access Logging**: Log all API access attempts (successful and failed)
3. **Regular Audits**: Review API key usage and access patterns
4. **Incident Response**: Prepare procedures for key compromise scenarios
## Conclusion
Proper API key management is essential for secure MCP Memory Service deployments. Follow the guidelines in this document to ensure your memory service remains secure while providing convenient access to authorized users and applications.
For additional security questions or advanced deployment scenarios, consult the [Multi-Client Deployment Guide](../deployment/multi-client-server.md) and [Service Installation Guide](service-installation.md).
```
--------------------------------------------------------------------------------
/tests/bridge/test_http_mcp_bridge.js:
--------------------------------------------------------------------------------
```javascript
/**
* Test suite for HTTP-MCP Bridge
*
* This comprehensive test suite ensures the HTTP-MCP bridge correctly:
* - Constructs URLs with proper base path handling
* - Handles various HTTP status codes correctly
* - Processes API responses according to actual server behavior
* - Manages errors and retries appropriately
*/
const assert = require('assert');
const sinon = require('sinon');
const path = require('path');
const HTTPMCPBridge = require(path.join(__dirname, '../../examples/http-mcp-bridge.js'));
describe('HTTP-MCP Bridge', () => {
let bridge;
let httpStub;
let httpsStub;
beforeEach(() => {
bridge = new HTTPMCPBridge();
bridge.endpoint = 'https://memory.local:8443/api';
bridge.apiKey = 'test-api-key';
});
afterEach(() => {
sinon.restore();
});
describe('URL Construction', () => {
it('should correctly resolve paths with base URL using URL constructor', () => {
// Test the new URL constructor logic that properly handles base paths
const testCases = [
{ path: 'memories', expected: 'https://memory.local:8443/api/memories' },
{ path: 'health', expected: 'https://memory.local:8443/api/health' },
{ path: 'search', expected: 'https://memory.local:8443/api/search' },
{ path: 'search?q=test&n_results=5', expected: 'https://memory.local:8443/api/search?q=test&n_results=5' }
];
for (const testCase of testCases) {
// Test the URL construction logic: ensure trailing slash, then use URL constructor
const baseUrl = bridge.endpoint.endsWith('/') ? bridge.endpoint : bridge.endpoint + '/';
const constructedUrl = new URL(testCase.path, baseUrl).toString();
assert.strictEqual(constructedUrl, testCase.expected,
`Failed for path: ${testCase.path}`);
}
});
it('should handle endpoints without trailing slash', () => {
bridge.endpoint = 'https://memory.local:8443/api';
// Test URL construction logic
const fullPath = '/memories';
const baseUrl = bridge.endpoint.endsWith('/') ?
bridge.endpoint.slice(0, -1) : bridge.endpoint;
const expectedUrl = 'https://memory.local:8443/api/memories';
assert.strictEqual(baseUrl + fullPath, expectedUrl);
});
it('should handle endpoints with trailing slash', () => {
bridge.endpoint = 'https://memory.local:8443/api/';
const fullPath = '/memories';
const baseUrl = bridge.endpoint.endsWith('/') ?
bridge.endpoint.slice(0, -1) : bridge.endpoint;
const expectedUrl = 'https://memory.local:8443/api/memories';
assert.strictEqual(baseUrl + fullPath, expectedUrl);
});
});
describe('Status Code Handling', () => {
it('should handle HTTP 200 with success=true for memory storage', async () => {
const mockResponse = {
statusCode: 200,
data: {
success: true,
message: 'Memory stored successfully',
content_hash: 'abc123'
}
};
sinon.stub(bridge, 'makeRequest').resolves(mockResponse);
const result = await bridge.storeMemory({
content: 'Test memory',
metadata: { tags: ['test'] }
});
assert.strictEqual(result.success, true);
assert.strictEqual(result.message, 'Memory stored successfully');
});
it('should handle HTTP 200 with success=false for duplicates', async () => {
const mockResponse = {
statusCode: 200,
data: {
success: false,
message: 'Duplicate content detected',
content_hash: 'abc123'
}
};
sinon.stub(bridge, 'makeRequest').resolves(mockResponse);
const result = await bridge.storeMemory({
content: 'Duplicate memory',
metadata: { tags: ['test'] }
});
assert.strictEqual(result.success, false);
assert.strictEqual(result.message, 'Duplicate content detected');
});
it('should handle HTTP 201 for backward compatibility', async () => {
const mockResponse = {
statusCode: 201,
data: {
success: true,
message: 'Created'
}
};
sinon.stub(bridge, 'makeRequest').resolves(mockResponse);
const result = await bridge.storeMemory({
content: 'Test memory',
metadata: { tags: ['test'] }
});
assert.strictEqual(result.success, true);
});
it('should handle HTTP 404 errors correctly', async () => {
const mockResponse = {
statusCode: 404,
data: {
detail: 'Not Found'
}
};
sinon.stub(bridge, 'makeRequest').resolves(mockResponse);
const result = await bridge.storeMemory({
content: 'Test memory',
metadata: { tags: ['test'] }
});
assert.strictEqual(result.success, false);
assert.strictEqual(result.message, 'Not Found');
});
});
describe('Health Check', () => {
it('should use health endpoint with proper URL construction', async () => {
let capturedPath;
sinon.stub(bridge, 'makeRequest').callsFake((path) => {
capturedPath = path;
return Promise.resolve({
statusCode: 200,
data: { status: 'healthy', version: '6.6.1' }
});
});
await bridge.checkHealth();
assert.strictEqual(capturedPath, 'health');
});
it('should return healthy status for HTTP 200', async () => {
sinon.stub(bridge, 'makeRequest').resolves({
statusCode: 200,
data: {
status: 'healthy',
storage_type: 'sqlite_vec',
statistics: { total_memories: 100 }
}
});
const result = await bridge.checkHealth();
assert.strictEqual(result.status, 'healthy');
assert.strictEqual(result.backend, 'sqlite_vec');
assert.deepStrictEqual(result.statistics, { total_memories: 100 });
});
it('should return unhealthy for non-200 status', async () => {
sinon.stub(bridge, 'makeRequest').resolves({
statusCode: 500,
data: { error: 'Internal Server Error' }
});
const result = await bridge.checkHealth();
assert.strictEqual(result.status, 'unhealthy');
assert.strictEqual(result.backend, 'unknown');
});
});
describe('Memory Retrieval', () => {
it('should handle successful memory retrieval', async () => {
sinon.stub(bridge, 'makeRequest').resolves({
statusCode: 200,
data: {
results: [
{
memory: {
content: 'Test memory',
tags: ['test'],
memory_type: 'note',
created_at_iso: '2025-08-24T12:00:00Z'
},
relevance_score: 0.95
}
]
}
});
const result = await bridge.retrieveMemory({
query: 'test',
n_results: 5
});
assert.strictEqual(result.memories.length, 1);
assert.strictEqual(result.memories[0].content, 'Test memory');
assert.strictEqual(result.memories[0].metadata.relevance_score, 0.95);
});
it('should handle empty results', async () => {
sinon.stub(bridge, 'makeRequest').resolves({
statusCode: 200,
data: { results: [] }
});
const result = await bridge.retrieveMemory({
query: 'nonexistent',
n_results: 5
});
assert.strictEqual(result.memories.length, 0);
});
});
describe('Error Handling', () => {
it('should handle network errors gracefully', async () => {
// Stub makeRequest which is what storeMemory actually calls
sinon.stub(bridge, 'makeRequest').rejects(
new Error('ECONNREFUSED')
);
const result = await bridge.storeMemory({
content: 'Test memory'
});
assert.strictEqual(result.success, false);
assert(result.message.includes('ECONNREFUSED'));
});
it('should retry on failure with exponential backoff', async () => {
const stub = sinon.stub(bridge, 'makeRequestInternal');
stub.onCall(0).rejects(new Error('Timeout'));
stub.onCall(1).rejects(new Error('Timeout'));
stub.onCall(2).resolves({
statusCode: 200,
data: { success: true }
});
// Mock the delay to avoid actual waiting in tests
const originalSetTimeout = global.setTimeout;
global.setTimeout = (fn, delay) => {
// Execute immediately but still track that delay was requested
originalSetTimeout(fn, 0);
return { delay };
};
const startTime = Date.now();
const result = await bridge.makeRequest('/test', 'GET', null, 3);
const duration = Date.now() - startTime;
// Restore original setTimeout
global.setTimeout = originalSetTimeout;
// Verify retry logic worked
assert.strictEqual(stub.callCount, 3);
assert.strictEqual(result.statusCode, 200);
}).timeout(5000);
});
describe('MCP Protocol Integration', () => {
it('should handle initialize method', async () => {
const request = {
method: 'initialize',
params: {},
id: 1
};
const response = await bridge.processRequest(request);
assert.strictEqual(response.jsonrpc, '2.0');
assert.strictEqual(response.id, 1);
assert(response.result.protocolVersion);
assert(response.result.capabilities);
});
it('should handle tools/list method', async () => {
const request = {
method: 'tools/list',
params: {},
id: 2
};
const response = await bridge.processRequest(request);
assert.strictEqual(response.id, 2);
assert(Array.isArray(response.result.tools));
assert(response.result.tools.length > 0);
// Verify all required tools are present
const toolNames = response.result.tools.map(t => t.name);
assert(toolNames.includes('store_memory'));
assert(toolNames.includes('retrieve_memory'));
assert(toolNames.includes('check_database_health'));
});
it('should handle tools/call for store_memory', async () => {
sinon.stub(bridge, 'storeMemory').resolves({
success: true,
message: 'Stored'
});
const request = {
method: 'tools/call',
params: {
name: 'store_memory',
arguments: {
content: 'Test',
metadata: { tags: ['test'] }
}
},
id: 3
};
const response = await bridge.processRequest(request);
assert.strictEqual(response.id, 3);
assert(response.result.content[0].text.includes('true'));
});
});
});
// Export for use in other test files
module.exports = { HTTPMCPBridge };
```
--------------------------------------------------------------------------------
/tests/api/test_operations.py:
--------------------------------------------------------------------------------
```python
# Copyright 2024 Heinrich Krupp
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Tests for core API operations.
Validates functionality, performance, and token efficiency of
search, store, and health operations.
"""
import pytest
import time
from mcp_memory_service.api import search, store, health
from mcp_memory_service.api.types import CompactSearchResult, CompactHealthInfo
from mcp_memory_service.api.client import reset_storage
@pytest.fixture(autouse=True)
def reset_client():
"""Reset storage client before each test."""
reset_storage()
yield
reset_storage()
class TestSearchOperation:
"""Tests for search() function."""
def test_search_basic(self):
"""Test basic search functionality."""
# Store some test memories first
hash1 = store("Test memory about authentication", tags=["test", "auth"])
hash2 = store("Test memory about database", tags=["test", "db"])
# Search for memories
result = search("authentication", limit=5)
assert isinstance(result, CompactSearchResult)
assert result.total >= 0
assert result.query == "authentication"
assert len(result.memories) <= 5
def test_search_with_limit(self):
"""Test search with different limits."""
# Store multiple memories
for i in range(10):
store(f"Test memory number {i}", tags=["test"])
# Search with limit
result = search("test", limit=3)
assert len(result.memories) <= 3
assert result.query == "test"
def test_search_with_tags(self):
"""Test search with tag filtering."""
# Store memories with different tags
store("Memory with tag1", tags=["tag1", "test"])
store("Memory with tag2", tags=["tag2", "test"])
store("Memory with both", tags=["tag1", "tag2", "test"])
# Search with tag filter
result = search("memory", limit=10, tags=["tag1"])
# Should only return memories with tag1
for memory in result.memories:
assert "tag1" in memory.tags
def test_search_empty_query(self):
"""Test that search rejects empty queries."""
with pytest.raises(ValueError, match="Query cannot be empty"):
search("")
with pytest.raises(ValueError, match="Query cannot be empty"):
search(" ")
def test_search_invalid_limit(self):
"""Test that search rejects invalid limits."""
with pytest.raises(ValueError, match="Limit must be at least 1"):
search("test", limit=0)
with pytest.raises(ValueError, match="Limit must be at least 1"):
search("test", limit=-1)
def test_search_returns_compact_format(self):
"""Test that search returns compact memory format."""
# Store a test memory
store("Test memory content", tags=["test"])
# Search
result = search("test", limit=1)
if result.memories:
memory = result.memories[0]
# Verify compact format
assert len(memory.hash) == 8, "Hash should be 8 characters"
assert len(memory.preview) <= 200, "Preview should be d200 chars"
assert isinstance(memory.tags, tuple), "Tags should be tuple"
assert isinstance(memory.created, float), "Created should be timestamp"
assert 0.0 <= memory.score <= 1.0, "Score should be 0-1"
def test_search_performance(self):
"""Test search performance meets targets."""
# Store some memories
for i in range(10):
store(f"Performance test memory {i}", tags=["perf"])
# Measure warm call performance
start = time.perf_counter()
result = search("performance", limit=5)
duration_ms = (time.perf_counter() - start) * 1000
# Should complete in <100ms for warm call
assert duration_ms < 100, f"Search too slow: {duration_ms:.1f}ms (target: <100ms)"
# Verify results returned
assert isinstance(result, CompactSearchResult)
class TestStoreOperation:
"""Tests for store() function."""
def test_store_basic(self):
"""Test basic store functionality."""
content = "This is a test memory"
hash_val = store(content)
assert isinstance(hash_val, str)
assert len(hash_val) == 8, "Should return 8-char hash"
def test_store_with_tags_list(self):
"""Test storing with list of tags."""
hash_val = store(
"Memory with tags",
tags=["tag1", "tag2", "tag3"]
)
assert isinstance(hash_val, str)
assert len(hash_val) == 8
# Verify stored by searching
result = search("Memory with tags", limit=1)
if result.memories:
assert "tag1" in result.memories[0].tags
def test_store_with_single_tag(self):
"""Test storing with single tag string."""
hash_val = store(
"Memory with single tag",
tags="singletag"
)
assert isinstance(hash_val, str)
assert len(hash_val) == 8
def test_store_with_memory_type(self):
"""Test storing with custom memory type."""
hash_val = store(
"Custom type memory",
tags=["test"],
memory_type="feature"
)
assert isinstance(hash_val, str)
assert len(hash_val) == 8
def test_store_empty_content(self):
"""Test that store rejects empty content."""
with pytest.raises(ValueError, match="Content cannot be empty"):
store("")
with pytest.raises(ValueError, match="Content cannot be empty"):
store(" ")
def test_store_returns_short_hash(self):
"""Test that store returns 8-char hash."""
hash_val = store("Test content for hash length")
assert len(hash_val) == 8
assert hash_val.isalnum() or all(c in '0123456789abcdef' for c in hash_val)
def test_store_duplicate_handling(self):
"""Test storing duplicate content."""
content = "Duplicate content test"
# Store same content twice
hash1 = store(content, tags=["test1"])
hash2 = store(content, tags=["test2"])
# Should return same hash (content is identical)
assert hash1 == hash2
def test_store_performance(self):
"""Test store performance meets targets."""
content = "Performance test memory content"
# Measure warm call performance
start = time.perf_counter()
hash_val = store(content, tags=["perf"])
duration_ms = (time.perf_counter() - start) * 1000
# Should complete in <50ms for warm call
assert duration_ms < 50, f"Store too slow: {duration_ms:.1f}ms (target: <50ms)"
# Verify hash returned
assert isinstance(hash_val, str)
class TestHealthOperation:
"""Tests for health() function."""
def test_health_basic(self):
"""Test basic health check."""
info = health()
assert isinstance(info, CompactHealthInfo)
assert info.status in ['healthy', 'degraded', 'error']
assert isinstance(info.count, int)
assert isinstance(info.backend, str)
def test_health_returns_valid_status(self):
"""Test that health returns valid status."""
info = health()
valid_statuses = ['healthy', 'degraded', 'error']
assert info.status in valid_statuses
def test_health_returns_backend_type(self):
"""Test that health returns backend type."""
info = health()
valid_backends = ['sqlite_vec', 'cloudflare', 'hybrid', 'unknown']
assert info.backend in valid_backends
def test_health_memory_count(self):
"""Test that health returns memory count."""
# Store some memories
for i in range(5):
store(f"Health test memory {i}", tags=["health"])
info = health()
# Count should be >= 5 (may have other memories)
assert info.count >= 5
def test_health_performance(self):
"""Test health check performance."""
# Measure warm call performance
start = time.perf_counter()
info = health()
duration_ms = (time.perf_counter() - start) * 1000
# Should complete in <20ms for warm call
assert duration_ms < 20, f"Health check too slow: {duration_ms:.1f}ms (target: <20ms)"
# Verify info returned
assert isinstance(info, CompactHealthInfo)
class TestTokenEfficiency:
"""Integration tests for token efficiency."""
def test_search_token_reduction(self):
"""Validate 85%+ token reduction for search."""
# Store test memories
for i in range(10):
store(f"Token test memory {i} with some content", tags=["token", "test"])
# Perform search
result = search("token", limit=5)
# Estimate token count (rough: 1 token H 4 characters)
result_str = str(result.memories)
estimated_tokens = len(result_str) / 4
# Target: ~385 tokens for 5 results (vs ~2,625 tokens, 85% reduction)
# Allow some margin: should be under 800 tokens
assert estimated_tokens < 800, \
f"Search result not efficient: {estimated_tokens:.0f} tokens (target: <800)"
# Verify we achieved significant reduction
reduction = 1 - (estimated_tokens / 2625)
assert reduction >= 0.70, \
f"Token reduction insufficient: {reduction:.1%} (target: e70%)"
def test_store_token_reduction(self):
"""Validate 90%+ token reduction for store."""
# Store operation itself is just parameters + hash return
content = "Test content for token efficiency"
tags = ["test", "efficiency"]
# Measure "token cost" of operation
# In practice: ~15 tokens (content + tags + function call)
param_str = f"store('{content}', tags={tags})"
estimated_tokens = len(param_str) / 4
# Target: ~15 tokens (vs ~150 for MCP tool, 90% reduction)
# Allow some margin
assert estimated_tokens < 50, \
f"Store call not efficient: {estimated_tokens:.0f} tokens (target: <50)"
def test_health_token_reduction(self):
"""Validate 84%+ token reduction for health check."""
info = health()
# Measure "token cost" of result
info_str = str(info)
estimated_tokens = len(info_str) / 4
# Target: ~20 tokens (vs ~125 for MCP tool, 84% reduction)
# Allow some margin
assert estimated_tokens < 40, \
f"Health info not efficient: {estimated_tokens:.0f} tokens (target: <40)"
class TestIntegration:
"""End-to-end integration tests."""
def test_store_and_search_workflow(self):
"""Test complete store -> search workflow."""
# Store memories
hash1 = store("Integration test memory 1", tags=["integration", "test"])
hash2 = store("Integration test memory 2", tags=["integration", "demo"])
assert len(hash1) == 8
assert len(hash2) == 8
# Search for stored memories
result = search("integration", limit=5)
assert result.total >= 2
assert any(m.hash in [hash1, hash2] for m in result.memories)
def test_multiple_operations_performance(self):
"""Test performance of multiple operations."""
start = time.perf_counter()
# Perform multiple operations
hash1 = store("Op 1", tags=["multi"])
hash2 = store("Op 2", tags=["multi"])
result = search("multi", limit=5)
info = health()
duration_ms = (time.perf_counter() - start) * 1000
# All operations should complete in <200ms
assert duration_ms < 200, f"Multiple ops too slow: {duration_ms:.1f}ms (target: <200ms)"
# Verify all operations succeeded
assert len(hash1) == 8
assert len(hash2) == 8
assert isinstance(result, CompactSearchResult)
assert isinstance(info, CompactHealthInfo)
def test_api_backward_compatibility(self):
"""Test that API doesn't break existing functionality."""
# This test ensures the API can coexist with existing MCP tools
# Store using new API
hash_val = store("Compatibility test", tags=["compat"])
# Should be searchable
result = search("compatibility", limit=1)
# Should find the stored memory
assert result.total >= 1
if result.memories:
assert hash_val == result.memories[0].hash
```