Skip to content

JoshSalomon/claude-ltm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Claude LTM - Long-Term Memory for Claude Code

Version Container License

A persistent memory system for Claude Code that enables Claude to remember important information across sessions. Built as a Claude Code plugin with container-based deployment.

Features

  • Automatic Context Loading - Top memories are loaded at session start
  • On-Demand Storage - Store important learnings with /ltm:remember
  • Semantic Search - Find memories by keyword with /ltm:recall
  • Smart Eviction - Graceful degradation through phases (Full → Hint → Abstract → Removed)
  • Difficulty Tracking - Memories from challenging tasks are prioritized
  • Git-Friendly - Human-readable markdown files with YAML frontmatter
  • Integrity Tools - Check and fix system health with /ltm:check and /ltm:fix
  • Extended Thinking Integration - Automatically consults memory in "think harder" and "ultrathink" modes

Quick Start

Prerequisites

Installation

Install LTM as a Claude Code plugin directly from GitHub:

# Add the LTM marketplace
claude plugin marketplace add https://github.com/JoshSalomon/claude-ltm.git

# Install the plugin
claude plugin install ltm@claude-ltm

# Enable extended thinking integration (run inside Claude Code)
# This adds memory consultation instructions to your project's CLAUDE.md
/ltm:init

The plugin automatically:

  • Runs an ephemeral container for each Claude Code session
  • Registers the MCP server for memory tools
  • Provides slash commands (/ltm:remember, /ltm:recall, /ltm:forget, /ltm:status, etc.)

The /ltm:init command enables:

  • Proactive memory search before debugging and implementing features
  • Extended thinking consultation that automatically searches memories during complex reasoning

Updating the Plugin

claude plugin update ltm@claude-ltm

Uninstalling

claude plugin uninstall ltm@claude-ltm
claude plugin marketplace remove claude-ltm

What Gets Created

Memory data is stored in your project's .claude/ltm/ directory:

your-project/
└── .claude/
    └── ltm/
        ├── index.json          # Memory index - git-tracked
        ├── stats.json          # Access statistics - git-ignored
        ├── state.json          # Session state - git-ignored
        ├── memories/           # Memory files - git-tracked
        │   └── mem_abc123.md
        └── archives/           # Archived content - git-tracked

Container Model

The MCP server runs in an ephemeral container using stdio transport:

  • A new container starts when Claude Code connects
  • The container is automatically removed when the session ends (--rm flag)
  • Container names are randomly generated by podman/docker
  • No persistent container management required

Git Configuration

Add these to your project's .gitignore:

.claude/ltm/stats.json
.claude/ltm/state.json

Memories (.claude/ltm/memories/ and .claude/ltm/index.json) are git-tracked for team sharing.


Usage

Slash Commands

Command Description
/ltm:init Add LTM integration instructions to project's CLAUDE.md
/ltm:remember Store a memory interactively
/ltm:remember <topic> Store a memory with the given topic
/ltm:recall <query> Search memories by keyword
/ltm:forget <id> Delete a memory by ID
/ltm:status Show system status
/ltm:help Show command summary
/ltm:list List all memories
/ltm:list --tag <tag> List memories with specific tag
/ltm:check Check system integrity
/ltm:fix Fix integrity issues
/ltm:fix --clean-archives Fix issues and remove orphaned archives

Examples

# Store a debugging solution
/ltm:remember Fix for authentication timeout

# Search for database-related memories
/ltm:recall database

# List all memories tagged with "api"
/ltm:list --tag api

# Check system health
/ltm:check

# Fix any integrity issues
/ltm:fix

MCP Tools

The LTM MCP server provides these tools (usable directly or via slash commands):

Tool Parameters Description
store_memory topic, content, tags?, auto_tag? Store a new memory
recall query, limit? Search memories
list_memories phase?, tag?, keyword? List with filters
get_memory id Get full memory content
forget id Delete a memory
ltm_status - Get system status
ltm_check - Check integrity
ltm_fix archive_orphans?, clean_orphaned_archives? Fix integrity issues

Architecture

Plugin Structure

claude-ltm/                 # Plugin root
├── .claude-plugin/
│   └── plugin.json         # Plugin manifest
├── .mcp.json               # MCP server configuration
├── hooks/
│   ├── hooks.json          # Hook definitions
│   ├── session_start.sh    # Load memories on session start
│   ├── post_tool_use.sh    # Track difficulty
│   ├── pre_compact.sh      # Save state before compaction
│   └── session_end.sh      # Persist and run eviction
├── commands/               # Slash commands
│   ├── remember.md
│   ├── recall.md
│   ├── forget.md
│   ├── status.md
│   ├── list.md
│   ├── check.md
│   ├── fix.md
│   └── help.md
├── server/                 # Container source
│   ├── mcp_server.py
│   ├── store.py
│   ├── priority.py
│   ├── eviction.py
│   └── requirements.txt
├── scripts/
│   └── run-mcp.sh          # Container launcher
├── Dockerfile
├── README.md
└── LICENSE

Deployment Model

  • Ephemeral containers: A new container starts for each Claude Code session and is removed when the session ends
  • Stdio transport: MCP communication uses standard input/output (not TCP)
  • Data persistence: Memory data is stored in the project's .claude/ltm/ directory, which is mounted into the container
  • No port mapping: Since stdio is used, no network ports are exposed

Storage Structure

.claude/ltm/
├── index.json           # Lightweight index for fast lookup (git-tracked)
├── stats.json           # Access statistics (git-ignored)
├── state.json           # Session state (git-ignored)
├── memories/            # Individual memory files (git-tracked)
│   └── mem_abc123.md    # Markdown with YAML frontmatter
└── archives/            # Evicted detailed content (git-tracked)

Memory File Format

Memories are stored as markdown files with YAML frontmatter:

---
id: "mem_abc123"
topic: "Fix database connection timeout"
tags:
  - database
  - debugging
phase: 0
difficulty: 0.8
created_at: "2026-01-15T10:30:00Z"
---
## Problem
The database connection was timing out after 30 seconds...

## Solution
Increased the connection pool size from 5 to 20...

Priority Algorithm

Memories are prioritized using:

priority = (difficulty * 0.4) + (recency * 0.3) + (frequency * 0.3)
  • Difficulty: Based on tool failures and context compaction
  • Recency: Sessions since last access (session-based, not time-based)
  • Frequency: How often the memory is accessed

Eviction Phases

When storage exceeds the threshold, low-priority memories are progressively reduced:

Phase Name Content Description
0 Full Complete content Original memory
1 Hint Summary only Detailed content archived
2 Abstract One-line summary Further reduced
3 Removed Archived only Deleted from active storage

Archived content is preserved in .claude/ltm/archives/ and can be restored.

GitOps & Multi-User Support

The LTM system is designed for GitOps workflows and multi-user collaboration:

Git-Tracked Files (shared across users/branches):

  • index.json - Lightweight index with memory metadata
  • memories/*.md - Memory content files (human-readable markdown)
  • archives/*.md - Archived content from evicted memories

Git-Ignored Files (local to each user/machine):

  • stats.json - Access statistics (access count, last accessed, priority scores)
  • state.json - Session state (session counter, configuration)

Why This Separation?

Concern Git-Tracked Git-Ignored
Memory content Yes -
Tags and metadata Yes -
Access patterns - Yes
Priority scores - Yes
Session counter - Yes

Benefits:

  1. No Merge Conflicts on Volatile Data - Access counts and priorities are local, so different users won't conflict on these frequently-changing values.

  2. Shared Knowledge Base - Memory content and tags are versioned, so team members can share learnings across branches and pull requests.

  3. Fresh Start on Clone - When you clone or switch branches, stats.json starts empty. Priority scores are recalculated from difficulty (stored in memory files) combined with local access patterns.

  4. Human-Readable Conflicts - When memory content conflicts occur, they're in markdown format and easy to resolve manually.

Multi-User Workflow:

# User A stores a memory
/ltm:remember Fix for database timeout

# User A commits and pushes
git add .claude/ltm/memories/ .claude/ltm/index.json
git commit -m "Add database timeout fix memory"
git push

# User B pulls and gets the memory
git pull
# Memory is now available, with fresh local access stats

Branch Merging:

Memories are typically additive - new memories get new IDs. When merging branches:

  • New memories from both branches are preserved
  • index.json changes are usually auto-mergeable
  • Memory file conflicts (rare) are human-readable markdown

Resolving index.json Merge Conflicts

If you get a merge conflict in index.json, here's how to resolve it:

Structure of index.json:

{
  "version": 1,
  "memories": {
    "mem_abc123": { "topic": "...", "tags": [...], "phase": 0 },
    "mem_def456": { "topic": "...", "tags": [...], "phase": 0 }
  }
}

Resolution steps:

  1. Keep all memories from both sides - Each memory has a unique ID (mem_XXXXXXXX). Include all memory entries from both versions.

  2. Verify memory files exist - Each memory ID in index.json should have a corresponding .claude/ltm/memories/mem_XXXXXXXX.md file.

Example conflict resolution:

// CONFLICT - both sides added different memories
<<<<<<< HEAD
  "memories": {
    "mem_abc123": { "topic": "Fix timeout", "tags": ["database"], "phase": 0 }
  }
=======
  "memories": {
    "mem_def456": { "topic": "API auth", "tags": ["api"], "phase": 0 }
  }
>>>>>>> feature-branch

// RESOLVED - keep both memories
  "memories": {
    "mem_abc123": { "topic": "Fix timeout", "tags": ["database"], "phase": 0 },
    "mem_def456": { "topic": "API auth", "tags": ["api"], "phase": 0 }
  }

After resolving: Run /ltm:check in Claude Code to verify integrity, and /ltm:fix if needed.


Configuration

Configuration is stored in .claude/ltm/state.json:

{
  "config": {
    "max_memories": 100,
    "memories_to_load": 10,
    "eviction_batch_size": 10
  }
}
Setting Default Description
max_memories 100 Maximum memories before eviction
memories_to_load 10 Memories loaded at session start
eviction_batch_size 10 Memories processed per eviction cycle

Development

Local Plugin Development

For contributing or testing changes locally:

# Clone the repository
git clone https://github.com/JoshSalomon/claude-ltm.git
cd claude-ltm

Building and Testing Container Changes

When modifying the MCP server code, build and test with a local container:

# Build local image
podman build -t localhost/ltm-mcp-server:latest .
# Or: docker build -t localhost/ltm-mcp-server:latest .

# Run Claude Code with the local image
LTM_MCP_IMAGE=localhost/ltm-mcp-server:latest claude --plugin-dir .

Running Tests

# Set up Python environment
python -m venv .venv
source .venv/bin/activate
pip install -r server/requirements.txt
pip install pytest pytest-cov pytest-asyncio

# Run tests
pytest server/tests/

# Run with coverage
pytest server/tests/ --cov=server --cov-report=term-missing

Project Structure

claude-ltm/
├── .claude-plugin/           # Plugin manifest
├── server/                   # MCP server source
│   ├── mcp_server.py
│   ├── store.py
│   ├── priority.py
│   ├── eviction.py
│   ├── token_counter.py
│   ├── requirements.txt
│   └── tests/                # Test suite
│       ├── conftest.py
│       ├── test_store.py
│       ├── test_priority.py
│       ├── test_eviction.py
│       ├── test_mcp_server.py
│       └── test_token_counter.py
├── hooks/                    # Hook scripts
│   ├── hooks.json
│   └── *.sh
├── commands/                 # Slash commands
│   └── *.md
├── scripts/                  # Utility scripts
│   └── run-mcp.sh
├── docs/                     # Documentation
│   ├── ARCHITECTURE.md
│   ├── PRD.md
│   └── TESTING.md
├── Dockerfile
└── README.md

Troubleshooting

MCP Server Not Responding

The MCP server runs in an ephemeral container. If it's not responding:

# Check if the container image exists
podman images | grep ltm-mcp-server

# Pull the latest image
podman pull quay.io/jsalomon/ltm-mcp-server:latest

# Test the container manually
echo '{}' | podman run -i --rm --userns=keep-id \
  -v "$(pwd)/.claude/ltm:/data:Z" \
  quay.io/jsalomon/ltm-mcp-server:latest

Integrity Issues

# In Claude Code, check for issues
/ltm:check

# Fix any issues found
/ltm:fix

Container Permission Errors

If using podman and encountering permission errors:

# The run-mcp.sh script uses --userns=keep-id automatically
# If running manually, include this flag:
podman run -i --rm --userns=keep-id -v "$(pwd)/.claude/ltm:/data:Z" quay.io/jsalomon/ltm-mcp-server:latest

Reset LTM Data

To completely reset the LTM system:

# Remove all memories and state
rm -rf .claude/ltm/memories/*
rm -rf .claude/ltm/archives/*
rm -f .claude/ltm/index.json
rm -f .claude/ltm/stats.json
rm -f .claude/ltm/state.json

Contributing

Contributions are welcome! Please read the documentation in docs/ before contributing.

License

Apache 2.0. See LICENSE file for details.

Links

About

Adding Long Term Memory for Claude code

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors