Claude Conversation Memory System

by adamkwhite

Not rated
GitHub

About

Provides searchable local storage for Claude conversation history, enabling context retrieval during sessions.

Details

Author
adamkwhite
Categories
Database, AI, Knowledge Base

Setup

Install Claude Conversation Memory System in your MCP client (Claude Desktop, Cursor, Windsurf, and others).

Repository: https://github.com/adamkwhite/claude-memory-mcp

Follow the installation instructions in the repository README, then restart your MCP client.

Provides searchable local storage for Claude conversation history, enabling context retrieval during sessions.

Universal Memory MCP β€” AI Conversation Memory

A Model Context Protocol (MCP) server that provides persistent, searchable conversation memory across multiple AI platforms. Store, search, and retrieve conversation history with fast full-text search powered by SQLite FTS5.

- πŸ”Fast full-text searchvia SQLite FTS5 with relevance ranking β€” ~10x faster than a linear scan (measured)
- 🏷️Automatic topic extractionβ€” 574+ unique topics across 2,000+ associations
- πŸ“ŠWeekly summarieswith insights and patterns
- πŸ—ƒοΈOrganized file storageby date and topic
- πŸ€–Multi-platform supportβ€” Claude, ChatGPT, Cursor AI, and custom formats
- πŸ”ŒMCP integrationfor Claude Desktop and Claude Code

- Python 3.10+ (CI runs 3.14)
- An MCP client β€” Claude Code, Claude Desktop, Codex, or anything else speaking MCP over stdio

uv tool install universal-memory-mcp # or: pipx install universal-memory-mcp

Notpip install: this is an application, and on Debian/Ubuntu and otherPEP 668systems installing one into the system interpreter fails witherror: externally-managed-environment. Inside a virtualenv you have already activated,pip install universal-memory-mcpis fine.

Then point your client at theuniversal-memory-mcpconsole script:

claude mcp add --transport stdio universal-memory-mcp -- universal-memory-mcp

Or write it into the config yourself β€” Claude Code and Claude Desktop:

{ "mcpServers": { "universal-memory-mcp": { "command": "universal-memory-mcp" } } }
[mcp_servers.universal-memory-mcp] command = "universal-memory-mcp"

The server name is yours to choose, but it sets the tool namespace your client exposes (mcp__<name>__). Conversations live in~/claude-memory/regardless, so renaming is safe.

Upgrading an install that points at a checkout?scripts/switch_mcp_config.pyrewrites both config formats in place β€” dry run by default,--applyto write.

git clone https://github.com/adamkwhite/universal-memory-mcp.git cd universal-memory-mcp python3 -m venv .venv && source .venv/bin/activate pip install -e . python3 tests/validate_system.py # optional: verify the install

Point your client at<checkout>/.venv/bin/python3 -m universal_memory_mcp.server_fastmcp. The package uses relative imports, so running the file directly cannot work β€”python3 src/universal_memory_mcp/server_fastmcp.pyfails withattempted relative import with no known parent package.

Your client starts the server for you; run it by hand only to debug.

universal-memory-mcp # installed from PyPI python3 -m universal_memory_mcp.server_fastmcp # from source
# Import conversations from JSON export python3 scripts/bulk_import_enhanced.py your_conversations.json

Full-text search across all stored conversations with relevance ranking. Query text is treated as literal Unicode terms, so punctuation and FTS5 operators do not change the query semantics. Results include conversation IDs for exact retrieval.

get_conversation(conversation_id, max_chars=12000)

Retrieve a stored conversation by an ID returned from a search tool. Content is read from the authoritative JSON store and truncated tomax_charsto protect the model context.max_charsmust be between 1 and 50,000.

Find conversations tagged with a specific topic.

Store a new conversation with automatic topic extraction and FTS indexing.

Generate insights and patterns from recent conversations.

View search engine statistics β€” index size, topic counts, and engine status.

update_conversation(conversation_id, content=None, title=None, add_tags=None, remove_tags=None, set_tags=None, conversation_type=None, session_id=None, user_id=None, change_note=None, record_audit=True)

Update fields on an existing conversation in place. Passconversation_idplus any subset of fields to change; unspecified fields are left alone. By default, the first line of stored content is rewritten with a self-documenting audit line β€”[update <iso-timestamp> β€” <change_note>]β€” chained across repeated updates. Ifchange_noteis omitted, it is derived from the changed fields.

Setrecord_audit=Falseonly for authoritative imports whose content must remain an exact replica of the source system. Normal interactive updates should retain the default audit record.

Tag operations:set_tagsreplaces the full tag list and is mutually exclusive withadd_tags/remove_tags(passset_tags=[]to clear all tags);add_tags/remove_tagsmutate the existing list.

Returns a status string. On success:Status: successplus a summary message and, when enabled, the audit line. On failure (malformed ID, conversation not found, no changes provided, conflicting tag ops, or an I/O error):Status: errorplus a message describing the problem.

Find conversations tagged with a specific tag β€” a universal metadata field populated by importers or set viaupdate_conversation(e.g.starred,archived,workspace:my-project). Exact match, case-sensitive. Requires SQLite FTS to be enabled; without it, returns an error message.

search_by_session_id(session_id, limit=10)

Find all conversations sharing asession_id, useful for reconstructing a multi-turn session that spans several stored conversation records (e.g. a Cursor working session, a Claude thread continued across days). Results are sorted chronologically (oldest first). Requires SQLite FTS to be enabled; without it, returns an error message.

search_by_conversation_type(conversation_type, limit=10)

Find conversations byconversation_type(e.g.chat,code,analysis). Exact match, most recent first. Requires SQLite FTS to be enabled; without it, returns an error message.

~/claude-memory/ β”œβ”€β”€ conversations/ β”‚ β”œβ”€β”€ 2025/ β”‚ β”‚ └── 06-june/ β”‚ β”‚ └── 2025-06-01_topic-name.md β”‚ β”œβ”€β”€ index.json # Search index β”‚ └── topics.json # Topic frequency └── summaries/ └── weekly/ └── week-2025-06-01.md
{ "mcpServers": { "universal-memory-mcp": { "command": "universal-memory-mcp" } } }

Installed from source rather than PyPI? Pointcommandat your virtualenv's interpreter and run the module:

{ "mcpServers": { "universal-memory-mcp": { "command": "/absolute/path/to/universal-memory-mcp/.venv/bin/python3", "args": ["-m", "universal_memory_mcp.server_fastmcp"] } } }

Upgrading from before the package move (#225):configs used to name the server script directly (src/server_fastmcp.py). That no longer works in any form β€” the modules moved undersrc/universal_memory_mcp/, and the package now uses relative imports, so running the file raisesattempted relative import with no known parent package. Switch to the console script or the-mform above.

Settings are resolved bysrc/universal_memory_mcp/config.py'sConfig.load(), consulted in this order (highest wins):
- Environment variables(CLAUDE_MEMORY_
/CLAUDE_MCP_)
- Config file(default~/.claude-memory/config.json)
- Platform profile(default,claude,chatgpt, orcursorβ€” selects a partial set of defaults, e.g.log_format)
- Built-in defaults

WhenCLAUDE_MEMORY_PATHis set explicitly, the path may live outside your home directory (e.g. a separate data drive on Windows:D:\claude-memory). Paths that arenotexplicitly configured are still restricted to the home or project directory for safety.

As an alternative to environment variables, settings can be placed in~/.claude-memory/config.json. The file is optional β€” a missing file falls back to platform-profile/built-in defaults. Example:

{ "storage_path": "~/claude-memory", "log_format": "json", "log_level": "INFO", "enable_sqlite": true, "console_output": false, "platform_profile": "default" }

Unknown keys in the file raise a configuration error rather than being silently ignored. Environment variables still override anything set here.

SQLite FTS5 search is enabled by default. On platforms where SQLite/FTS5 is unavailable (e.g. some Windows Python builds), disable it to fall back to JSON-based linear search:

export CLAUDE_MEMORY_DISABLE_SQLITE=true

Switch between human-readable text logs (default) and structured JSON logs for production:

# JSON format (for production log aggregation) export CLAUDE_MCP_LOG_FORMAT=json # Text format (default, for development) export CLAUDE_MCP_LOG_FORMAT=text
{ "timestamp": "2025-01-15T10:30:45", "level": "INFO", "logger": "claude_memory_mcp", "function": "add_conversation", "line": 145, "message": "Added conversation successfully", "context": { "type": "performance", "duration_seconds": 0.045, "conversation_id": "conv_abc123" } }

- Production deployments with log aggregation (Datadog, ELK, CloudWatch)
- Automated monitoring and alerting
- Structured log analysis and querying
- Performance tracking and debugging

Seedocs/json-logging.mdfor detailed JSON logging documentation.

universal-memory-mcp/ β”œβ”€β”€ src/ β”‚ β”œβ”€β”€ server_fastmcp.py # Main MCP server β”‚ β”œβ”€β”€ conversation_memory.py # Core memory engine + SQLite FTS5 β”‚ β”œβ”€β”€ format_detector.py # Auto-detect AI platform format β”‚ β”œβ”€β”€ validators.py # Input validation β”‚ β”œβ”€β”€ logging_config.py # Structured logging (text/JSON) β”‚ β”œβ”€β”€ importers/ # Platform-specific importers β”‚ β”‚ β”œβ”€β”€ chatgpt_importer.py β”‚ β”‚ β”œβ”€β”€ claude_importer.py β”‚ β”‚ β”œβ”€β”€ cursor_importer.py β”‚ β”‚ └── generic_importer.py β”‚ └── schemas/ # JSON schema validation β”œβ”€β”€ tests/ # 435 tests, 98.68% coverage β”œβ”€β”€ data/ # Consolidated app data β”œβ”€β”€ scripts/ # Import and utility scripts └── docs/ # Documentation

scripts/benchmark_search.pywas broken (unawaited async calls, measuring coroutine construction instead of real search time) from October 2025 until this was found and fixed. The previous numbers below were never actually measured and have been replaced with real ones. Reproduce with:

python scripts/generate_test_data.py --conversations 159 python scripts/benchmark_search.py --storage-path ~/claude-memory-test --iterations 5

Measured on a 159-conversation / 7.7MB local dataset (WSL2, Python 3.12) β€” treat as order-of-magnitude, not a precise SLA, results vary by machine:

- Search Speed (SQLite FTS5): mean 15–18ms, median 10–13ms per query, range 0.5–82ms across 12 query types (was claimed 0.2–0.5ms; that figure was never measured)
- Search vs. linear JSON scan: SQLite FTS5 is ~10x faster (mean 14.7ms vs 154.2ms; median 10.5ms vs 152.0ms) β€” the old "4.4x" claim had the right direction but was also never actually measured
- Topic Search: mean 3.4ms, median 2.5ms (was claimed 0.3–0.4ms; that figure was never measured)
- Write Speed: mean 14ms, median 14ms per ~49KB conversation, SQLite indexing included (was claimed ~33ms; that figure was never measured)
- Capacity: 371 conversations in production use over 10 months
- Test Coverage: 98.68% (435 tests) β€” 0 code smells, 0 security hotspots (SonarCloud verified)

Last benchmarked: July 2026 |Detailed Report

Note for Developers: Performance benchmarks create a~/claude-memory-testdirectory for isolated testing. Normal MCP usage only uses~/claude-memory/. If you see~/claude-memory-test, it can be safely deleted.

# Technical topics search_conversations("terraform azure") search_conversations("mcp server setup") search_conversations("python debugging") # Project discussions search_conversations("interview preparation") search_conversations("product management") search_conversations("architecture decisions") # Specific problems search_conversations("dependency issues") search_conversations("authentication error") search_conversations("deployment configuration")

- Topic Extraction: Modify_extract_topics()inConversationMemoryServer
- Search Algorithm: Enhancesearch_conversations()method
- Summary Generation: Improvegenerate_weekly_summary()logic

# Run validation suite python3 tests/validate_system.py # Run full test suite with coverage python3 -m pytest tests/ --cov=src --cov-report=term # Import test data python3 scripts/bulk_import_enhanced.py test_data.json --dry-run

Test Data Storage (Developers Only): If you run performance benchmarks or test data generators, they create a~/claude-memory-testdirectory to isolate test data from your production~/claude-memorydirectory.This is only for development/testing- normal MCP usage does not create this directory.

To clean up test data after running benchmarks:

MCP Import Errors:themcpdependency comes with the package, so this normally means the server is running under an interpreter that does not have it. Check which one your MCP config invokes: theuniversal-memory-mcpconsole script fromuv tool/pipx, or your virtualenv'spython3 -m universal_memory_mcp.server_fastmcpβ€” not a bare systempython3.

- Check conversation indexing:ls ~/claude-memory/conversations/index.json
- Verify file permissions
- Run validation:python3 tests/validate_system.py

- Ensure all datetime objects use consistent timezone handling
- Recent fix addresses timezone-aware vs naive comparison

- Python: 3.10+ (CI runs 3.14)
- Disk Space: ~10MB per 100 conversations
- Memory: <100MB RAM usage
- OS: Linux/WSL and Windows are both verified in CI on every PR (Ubuntu +windows-latest). macOS is expected to work but is not covered by a CI runner.
- Fork the repository
- Create a feature branch:git checkout -b feature-name
- Commit changes:git commit -am 'Add feature'
- Push to branch:git push origin feature-name
- Submit a Pull Request

A note for fork PRs:GitHub does not give forks access to repository secrets, so the SonarCloud scan and the performance-results comment areskippedon your PR rather than run. That is expected and is not something you can or should fix β€” the test suite, linting, CodeQL and the Windows run all still execute normally, and coverage on your changes is checked when the branch lands onmain. If you see those two skipped, nothing is wrong.

Publishing is tag-gated and usesTrusted Publishing(OIDC) β€” there is no PyPI token stored in this repo..github/workflows/publish.ymlfires only on avX.Y.Ztag.

One-time setup on PyPI (publisher settings for the project, or apendingpublisher while the name is still unclaimed):

# 1. bump version in pyproject.toml, commit, merge to main # 2. tag the merged commit β€” the workflow refuses a tag that disagrees with pyproject git tag v0.1.0 && git push origin v0.1.0

The workflow builds, runstwine check, installs the wheel into a clean venv and asserts that every module imports and that no generic top-level name leaked, then publishes. Add required reviewers to thepypienvironment in repo settings for a manual approval gate as well.

Rehearse on TestPyPI before the first real uploadβ€” the first upload claims the name permanently, and a version number can never be reused:

rm -rf dist && uv build uv run --with twine --no-project twine upload --repository testpypi dist/ # TestPyPI does not mirror mcp/jsonschema/aiofiles, so pull deps from real PyPI: uv pip install --index-url https://test.pypi.org/simple/ \ --extra-index-url https://pypi.org/simple/ universal-memory-mcp

MIT License - see LICENSE file for details

No reviews yet β€” be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.