Sandbox
@richardwhiteii/rlm

MCP server for recursive context analysis in Claude Code

RLM gives Claude Code a way to treat large inputs as external variables instead of prompt text. It loads context, inspects structure, chunks content, runs sub-queries, and aggregates results through MCP tools.

49 stars6 forksPythonUpdated 4mo ago
Who it's for

Builders who want Claude Code to inspect very large files, logs, and codebases without running out of context.

What it delivers

You can analyze massive inputs with Claude Code while keeping the prompt small and the workflow repeatable.

What it does

Load context as an external variable

Stores large text under a context name instead of putting it directly in the prompt.

Inspect and chunk content

Shows structure and splits content by lines, characters, or paragraphs so Claude can work on smaller pieces.

Recursive sub-queries

Lets Claude call smaller analysis passes on chunks, with optional multi-step recursion.

Batch processing and aggregation

Runs sub-queries across many chunks in parallel and stores results for later retrieval.

Deterministic Python execution

Provides `rlm_exec` for sandboxed Python against a loaded context when regex or parsing is enough.

Claude Code auto-detection

Includes hooks, skills, and a `CLAUDE.md` example so Claude can reach for RLM automatically on large files.

Local inference support

Can use Ollama for free local sub-queries instead of the default Claude SDK provider.

How to get it

  1. 1Run
    git clone https://github.com/richardwhiteii/rlm.git
    cd rlm
    uv sync
  2. 2Or with pip
    python -m venv .venv
    source .venv/bin/activate
    pip install -e .
  3. 3Option 1: Quick Setup (recommended)
    # From the rlm directory
    claude mcp add rlm -s user -- uv run --directory "$(pwd)" python -m src.rlm_mcp_server
  4. 4First set environment variables, then add
    export RLM_DATA_DIR="$HOME/.rlm-data"
    export OLLAMA_URL="http://localhost:11434"
    
    claude mcp add rlm -s user -- uv run --directory "$(pwd)" python -m src.rlm_mcp_server

README

RLM MCP Server

Recursive Language Model patterns for Claude Code — handle massive contexts (10M+ tokens) by treating them as external variables.

Based on: https://arxiv.org/html/2512.24601v1

How Users Interact

You don't call RLM tools directly. You ask Claude to analyze large files, and Claude uses RLM behind the scenes.

Example:

  • You say: "Analyze this 2MB log file for errors"
  • Claude uses RLM tools internally
  • You get: "I found 3 error patterns: database timeouts (47), auth failures (23)..."

Core Idea

Instead of feeding massive contexts directly into the LLM:

  1. Load context as external variable (stays out of prompt)
  2. Inspect structure programmatically
  3. Chunk strategically (lines, chars, or paragraphs)
  4. Sub-query recursively on chunks
  5. Aggregate results for final synthesis

Quick Start

Installation

git clone https://github.com/richardwhiteii/rlm.git
cd rlm
uv sync

Or with pip:

python -m venv .venv
source .venv/bin/activate
pip install -e .

Configure for Claude Code

Option 1: Quick Setup (recommended)

# From the rlm directory
claude mcp add rlm -s user -- uv run --directory "$(pwd)" python -m src.rlm_mcp_server

This adds RLM globally (-s user) so it's available in all your Claude Code sessions.

Option 2: With Ollama (free local inference)

First set environment variables, then add:

export RLM_DATA_DIR="$HOME/.rlm-data"
export OLLAMA_URL="http://localhost:11434"

claude mcp add rlm -s user -- uv run --directory "$(pwd)" python -m src.rlm_mcp_server

Option 3: Manual JSON config

Add to ~/.claude/.mcp.json for full control:

{
  "mcpServers": {
    "rlm": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/rlm", "python", "-m", "src.rlm_mcp_server"],
      "env": {
        "RLM_DATA_DIR": "/path/to/.rlm-data",
        "OLLAMA_URL": "http://localhost:11434"
      }
    }
  }
}

Note: Replace /path/to/rlm with your actual installation path (run pwd in the rlm directory).

Enable Auto-Detection

Enable Claude to use RLM tools automatically without manual invocation:

1. CLAUDE.md Integration Copy CLAUDE.md.example content to your project's CLAUDE.md (or ~/.claude/CLAUDE.md for global) to teach Claude when to reach for RLM tools automatically.

2. Hook Installation Copy the .claude/hooks/ directory to your project to auto-suggest RLM when reading files >25KB:

cp -r .claude/hooks/ /Users/your_username/your-project/.claude/hooks/

The hook provides guidance but doesn't block reads.

3. Skill Reference Copy the .claude/skills/ directory for comprehensive RLM guidance:

cp -r .claude/skills/ /Users/your_username/your-project/.claude/skills/

With these in place, Claude will autonomously detect when to use RLM instead of reading large files directly into context.

Tools

These tools are used by Claude internally when processing large contexts. You don't call them directly—you just ask Claude to analyze large files.

ToolPurpose
rlm_auto_analyzeOne-step analysis — auto-detects type, chunks, and queries
rlm_load_contextLoad context as external variable
rlm_inspect_contextGet structure info without loading into prompt
rlm_chunk_contextChunk by lines/chars/paragraphs
rlm_get_chunkRetrieve specific chunk
rlm_filter_contextFilter with regex (keep/remove matching lines)
rlm_execExecute Python code against loaded context (sandboxed)
rlm_sub_queryMake sub-LLM call on chunk
rlm_sub_query_batchProcess multiple chunks in parallel
rlm_store_resultStore sub-call result for aggregation
rlm_get_resultsRetrieve stored results
rlm_list_contextsList all loaded contexts

Quick Analysis with rlm_auto_analyze

For most use cases, Claude uses rlm_auto_analyze — it handles everything automatically:

rlm_auto_analyze(
    name="my_file",
    content=file_content,
    goal="find_bugs"  # or: summarize, extract_structure, security_audit, answer:<question>
)

What it does automatically:

  1. Detects content type (Python, JSON, Markdown, logs, prose, code)
  2. Selects optimal chunking strategy
  3. Adapts the query for the content type
  4. Runs parallel sub-queries
  5. Returns aggregated results

Supported goals:

GoalDescription
summarizeSummarize content purpose and key points
find_bugsIdentify errors, issues, potential problems
extract_structureList functions, classes, schema, headings
security_auditFind vulnerabilities and security issues
answer:<question>Answer a custom question about the content

Programmatic Analysis with rlm_exec

For deterministic pattern matching and data extraction, Claude can use rlm_exec to run Python code directly against a loaded context. This is closer to the paper's REPL approach and provides full control over analysis logic.

Tool: rlm_exec

Purpose: Execute arbitrary Python code against a loaded context in a sandboxed subprocess.

Parameters:

  • code (required): Python code to execute. Set the result variable to capture output.
  • context_name (required): Name of a previously loaded context.
  • timeout (optional, default 30): Maximum execution time in seconds.

Features:

  • Context available as read-only context variable
  • Pre-imported modules: re, json, collections
  • Subprocess isolation (won't crash the server)
  • Timeout enforcement
  • Works on any system with Python (no Docker needed)

Example — Finding patterns in a loaded context:

# After loading a context
rlm_exec(
    code="""
import re
amounts = re.findall(r'\$[\d,]+', context)
result = {'count': len(amounts), 'sample': amounts[:5]}
""",
    context_name="bill"
)

Example Response:

{
  "result": {
    "count": 1247,
    "sample": ["$500", "$1,000", "$250,000", "$100,000", "$50"]
  },
  "stdout": "",
  "stderr": "",
  "return_code": 0,
  "timed_out": false
}

Example — Extracting structured data:

rlm_exec(
    code="""
import re
import json

# Find all email addresses
emails = re.findall(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', context)

# Count by domain
from collections import Counter
domains = [e.split('@')[1] for e in emails]
domain_counts = Counter(domains)

result = {
    'total_emails': len(emails),
    'unique_domains': len(domain_counts),
    'top_domains': domain_counts.most_common(5)
}
""",
    context_name="dataset",
    timeout=60
)

When to use rlm_exec vs rlm_sub_query:

Use CaseToolWhy
Extract all dates, IDs, amountsrlm_execRegex is deterministic and fast
Find security vulnerabilitiesrlm_sub_queryRequires reasoning and context
Parse JSON/XML structurerlm_execStandard libraries work perfectly
Summarize themes or tonerlm_sub_queryNatural language understanding needed
Count word frequenciesrlm_execSimple computation, no AI needed
Answer "Why did X happen?"rlm_sub_queryRequires inference and reasoning

Tip: For large contexts, combine both — use rlm_exec to filter/extract, then rlm_sub_query for semantic analysis of filtered results.

Providers

By default, sub-queries use Claude Haiku 4.5 via the Claude Agent SDK. This works out-of-the-box if you have a Claude API key configured.

ProviderDefault ModelCostUse Case
claude-sdkclaude-haiku-4-5~$0.80/1M inputDefault, works everywhere
ollamaolmo-3.1:32b$0Local inference, requires Ollama

Recursive Sub-Queries

The rlm_sub_query and rlm_sub_query_batch tools support hierarchical decomposition via the max_depth parameter:

  • max_depth=0 (default): Flat call, no recursion
  • max_depth=1-5: Sub-LLM can use RLM tools (chunk, filter, sub_query, etc.)

Example: Analyzing a massive codebase with 2-level recursion:

rlm_sub_query(
    query="Find all security vulnerabilities",
    context_name="codebase",
    chunk_index=0,
    max_depth=2  # Allow sub-queries to further decompose
)

How it works:

  1. When max_depth > 0, the sub-LLM receives RLM tools in its function calling context
  2. If the sub-LLM decides to use a tool (e.g., rlm_chunk_context), the agent loop handles it
  3. Each recursive call decrements the depth limit until max_depth is reached
  4. The response includes recursion metadata: depth_reached and call_trace

Recommended for recursive calls: Use a local model like gemma3:27b via Ollama to avoid cost escalation from deep recursion.

Using Ollama (Free Local Inference)

With Ollama installed locally, you can run sub-queries at zero cost:

  1. Install Ollama and pull a model:

    ollama pull gemma3:27b
    
  2. Add Ollama URL to your MCP config:

    {
      "mcpServers": {
        "rlm": {
          "command": "uv",
          "args": ["run", "--directory", "/Users/your_username/projects/rlm", "python", "-m", "src.rlm_mcp_server"],
          "env": {
            "RLM_DATA_DIR": "/Users/your_username/.rlm-data",
            "OLLAMA_URL": "http://localhost:11434"
          }
        }
      }
    }
    
  3. Specify provider in sub-queries:

    rlm_sub_query(
        query="Summarize this section",
        context_name="my_doc",
        chunk_index=0,
        provider="ollama"  # Use local Ollama instead of default claude-sdk
    )
    

Or for batch processing:

rlm_sub_query_batch(
    query="Extract key points",
    context_name="my_doc",
    chunk_indices=[0, 1, 2, 3],
    provider="ollama",  # Use local Ollama instead of default claude-sdk
    concurrency=4
)

Usage Examples

Basic Pattern

# 1. Load a large document
rlm_load_context(name="report", content=<large document>)

# 2. Inspect structure
rlm_inspect_context(name="report", preview_chars=500)

# 3. Chunk into manageable pieces
rlm_chunk_context(name="report", strategy="paragraphs", size=1)

# 4. Sub-query chunks in parallel
rlm_sub_query_batch(
    query="What is the main topic? Reply in one sentence.",
    context_name="report",
    chunk_indices=[0, 1, 2, 3],
    concurrency=4  # uses claude-sdk by default
)

# 5. Store results for aggregation
rlm_store_result(name="topics", result=<response>)

# 6. Retrieve all results
rlm_get_results(name="topics")

Analyzing Encyclopedia Britannica (11MB)

The flagship example of RLM capabilities — processing the Encyclopedia Britannica, 11th Edition from Project Gutenberg:

# Load the full encyclopedia (11MB, ~2M tokens)
content = open("docs/encyclopedia/merged_encyclopedia.txt").read()
rlm_load_context(name="encyclopedia", content=content)

# Inspect
rlm_inspect_context(name="encyclopedia")
# → 11MB, 184K lines, ~2M tokens

# Chunk for processing
rlm_chunk_context(name="encyclopedia", strategy="paragraphs", size=30)

# Query across the corpus
rlm_sub_query_batch(
    query="Summarize the main topics in this section",
    context_name="encyclopedia",
    chunk_indices=[0, 50, 100, 150],
    provider="claude-sdk"  # or "ollama" for free local inference
)

Extract Topic Catalog:

# Filter for specific subject
rlm_filter_context(
    name="encyclopedia",
    output_name="botany",
    pattern="(?i)(botan|plant|flora|flower|genus)",
    mode="keep"
)

# Analyze filtered content
rlm_auto_analyze(
    name="botany_analysis",
    content=filtered_content,
    goal="answer:List all botanical articles with brief descriptions"
)
MetricValue
File size11 MB
Lines184,000
Tokens~2M
Processing cost$0 (Ollama) or ~$1.60 (Haiku)

Data Storage

$RLM_DATA_DIR/
├── contexts/     # Raw contexts (.txt + .meta.json)
├── chunks/       # Chunked versions (by context name)
└── results/      # Stored sub-call results (.jsonl)

Contexts persist across sessions. Chunked contexts are cached for reuse.

Architecture

Claude Code
    │
    ▼
RLM MCP Server
    │
    ├─► claude-sdk (Haiku 4.5) ─► Anthropic API
    │
    └─► ollama ─► Local LLM (gemma3:27b, llama3, etc.)

The key insight: context stays external, not in your prompt. Claude orchestrates; sub-models analyze.

For Contributors: Learning the Codebase

Use these prompts with Claude Code to explore the codebase and learn RLM patterns. The code is the single source of truth.

Understanding the Tools

Read src/rlm_mcp_server.py and list all RLM tools with their parameters and purpose.
Explain the chunking strategies available in rlm_chunk_context.
When would I use each one?
What's the difference between rlm_sub_query and rlm_sub_query_batch?
Show me the implementation.

Understanding the Architecture

Read src/rlm_mcp_server.py and explain how contexts are stored and persisted.
Where does the data live?
How does the claude-sdk provider extract text from responses?
Walk me through _call_claude_sdk.
What happens when I call rlm_load_context? Trace the full flow.

Hands-On Learning

Load the README as a context, chunk it by paragraphs,
and run a sub-query on the first chunk to summarize it.
Show me how to process a large file in parallel using rlm_sub_query_batch.
Use a real example.
I have a 1MB log file. Walk me through the RLM pattern to extract all errors.

Extending RLM

Read the test file and explain what scenarios are covered.
What edge cases should I be aware of?
How would I add a new chunking strategy (e.g., by regex delimiter)?
Show me where to modify the code.
How would I add a new provider (e.g., OpenAI)?
What functions need to change?

Test Corpus: Encyclopedia Britannica

The repository includes excerpts from the Encyclopedia Britannica, 11th Edition (1910-1911) from Project Gutenberg for testing RLM capabilities on large reference documents.

Included Files

FileSizeDescription
docs/encyclopedia/merged_encyclopedia.txt11MBAll slices merged (~2M tokens)

Using for Testing

# Load the full encyclopedia
content = open("docs/encyclopedia/merged_encyclopedia.txt").read()
rlm_load_context(name="encyclopedia", content=content)

# Inspect
rlm_inspect_context(name="encyclopedia")
# → 11MB, 184K lines, ~2M tokens

# Chunk for processing
rlm_chunk_context(name="encyclopedia", strategy="paragraphs", size=30)

# Query across the corpus
rlm_sub_query_batch(
    query="Summarize the main topics in this section",
    context_name="encyclopedia",
    chunk_indices=[0, 50, 100, 150],
    provider="claude-sdk"  # or "ollama" for free local inference
)

Example: Extract Topic Catalog

# Filter for specific subject
rlm_filter_context(
    name="encyclopedia",
    output_name="botany",
    pattern="(?i)(botan|plant|flora|flower|genus)",
    mode="keep"
)

# Analyze filtered content
rlm_auto_analyze(
    name="botany_analysis",
    content=filtered_content,
    goal="answer:List all botanical articles with brief descriptions"
)

Download More Slices

Additional encyclopedia volumes available from Project Gutenberg:

Attribution

Encyclopedia Britannica, 11th Edition (1910-1911) sourced from Project Gutenberg. Public domain.

License

MIT

Files in the repo

Repository payload12 top-level entries
  • .claude
  • docs
  • src
  • tests
  • .gitignore
  • .mcp.json
  • CLAUDE.md.example
  • LICENSE
  • pyproject.toml
  • README.md
  • ROADMAP.md
  • uv.lock

Discussion (0)

Ask about usage, or say what you built with it

Sign in to join the discussion.

No comments yet. Be the first to say what this is good for.

More connectors

Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface

86k

High-performance code intelligence MCP server. Indexes codebases into a persistent knowledge graph — average repo in milliseconds. 158 languages, sub-ms queries, 99% fewer tokens. Single static binary, zero dependencies.

43k

Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code

14k
okf-memory/
okf-agent-memory

Git-native persistent memory for AI coding agents. Implements Google OKF v0.2 with sub-300µs in-memory BM25 search, embedded MCP server, and progressive disclosure. Slashes token bloat by 80% with zero external databases or dependencies. Built in pure Go.

547
tirth8205/
code-review-graph

Local-first code intelligence graph for MCP and CLI. Builds a persistent map of your codebase so AI coding tools read only what matters, with benchmarked context reductions on reviews and large-repo workflows.

31k
2akouwu/
reverify

Stop your AI from making things up — it proposes, deterministic tools decide, every claim checked against ground truth with evidence. Grounded facts and context survive resets. Reverse engineering is the proving ground. MCP server + CLI.

1.1k