claudecode-rlm 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,132 +1,81 @@
1
1
  # claudecode-rlm
2
2
 
3
- **Advanced RLM (Recursive Language Model) plugin for [Claude Code](https://github.com/tekcin/claude-code) - Knowledge graph-based context storage with 74x faster reads.**
3
+ **MCP server for Claude Code - Knowledge graph-based context storage with 74x faster reads.**
4
4
 
5
5
  **Author:** Michael Thornton (tekcin@yahoo.com)
6
6
  **Repository:** https://github.com/tekcin/claudecode-rlm
7
7
 
8
8
  ## Features
9
9
 
10
- - **Automatic Context Injection** - Proactively injects relevant context when reference patterns detected
10
+ - **MCP Server** - Works directly with Claude Code via Model Context Protocol
11
11
  - **Knowledge Graph Storage** - Hierarchical context storage (Document → Section → Chunk → Entity)
12
12
  - **74x Faster Reads** - LRU cache with inverted index for instant retrieval
13
- - **40+ Reference Patterns** - Extended pattern detection for context references
14
- - **Enhanced Hybrid Search** - Keyword matching with recency weighting and entity boost
15
13
  - **Entity Extraction** - Automatic extraction of code elements, files, and concepts
16
- - **Custom Tools** - `graph_query`, `memory_enhanced`, `list_entities`, `graph_stats`
14
+ - **Enhanced Search** - Keyword matching with recency weighting and entity boost
17
15
 
18
16
  ## Installation
19
17
 
20
18
  ```bash
21
- npm install claudecode-rlm
19
+ npm install -g claudecode-rlm
22
20
  ```
23
21
 
24
- Or add to your Claude Code configuration:
22
+ ## Usage with Claude Code
25
23
 
26
- ```jsonc
27
- // .claude/claudecode.jsonc
24
+ Add to your Claude Code MCP settings (`~/.claude/claude_desktop_config.json`):
25
+
26
+ ```json
28
27
  {
29
- "plugin": ["claudecode-rlm"],
30
- "claudecode-rlm": {
31
- "graph": {
32
- "enabled": true,
33
- "auto_ingest": true
34
- },
35
- "enhanced_search": {
36
- "additional_patterns": true,
37
- "recency_weight": 0.3,
38
- "entity_boost": 0.2
28
+ "mcpServers": {
29
+ "claudecode-rlm": {
30
+ "command": "claudecode-rlm",
31
+ "env": {
32
+ "CLAUDECODE_RLM_WORKDIR": "/path/to/your/project"
33
+ }
39
34
  }
40
35
  }
41
36
  }
42
37
  ```
43
38
 
44
- ## Performance
45
-
46
- Benchmarks comparing basic vs optimized storage (10,000 nodes):
39
+ Or with npx (no global install):
47
40
 
48
- | Operation | Basic | Optimized | Speedup |
49
- |-----------|-------|-----------|---------|
50
- | Node reads | 21.3µs | 0.3µs | **74x** |
51
- | Get by type | 1.1ms | 21µs | **54x** |
52
- | Bulk insert | 6.07s | 1.21s | **5x** |
53
- | Search | 0.45ms | 0.65ms | ~1x |
41
+ ```json
42
+ {
43
+ "mcpServers": {
44
+ "claudecode-rlm": {
45
+ "command": "npx",
46
+ "args": ["claudecode-rlm"],
47
+ "env": {
48
+ "CLAUDECODE_RLM_WORKDIR": "/path/to/your/project"
49
+ }
50
+ }
51
+ }
52
+ }
53
+ ```
54
54
 
55
- The LRU cache achieves **100% hit rate** after initial population, ideal for read-heavy RLM workloads.
55
+ ## Available Tools
56
56
 
57
- ## Architecture
57
+ ### memory_store
58
58
 
59
- ### Knowledge Graph Structure
59
+ Store content in the knowledge graph for later retrieval.
60
60
 
61
61
  ```
62
- Document (archived context block)
63
- ├── Section (markdown header or paragraph group)
64
- │ ├── Chunk (retrieval unit, ~300 chars)
65
- │ │ ├── Entity (code element)
66
- │ │ ├── Entity (file path)
67
- │ │ └── Entity (concept)
68
- │ └── Chunk
69
- │ └── [FOLLOWS] → next chunk
70
- └── Section
62
+ Store important context, decisions, or code discussions.
63
+ Automatically extracts entities and creates searchable chunks.
71
64
  ```
72
65
 
73
- ### Storage Optimizations
74
-
75
- 1. **LRU Cache** (2000 nodes/session) - Eliminates repeated disk reads
76
- 2. **Inverted Index** - O(1) keyword lookup instead of O(n) scan
77
- 3. **Batched Writes** - Flushes every 100ms or 50 ops, reducing I/O ~90%
66
+ ### memory_search
78
67
 
79
- ### Reference Patterns
68
+ Search stored content with enhanced scoring.
80
69
 
81
- The plugin detects 40+ patterns that trigger proactive context retrieval:
82
-
83
- ```javascript
84
- // Explicit memory references
85
- "do you remember..."
86
- "we discussed..."
87
- "earlier we..."
88
-
89
- // Task references
90
- "what did we do..."
91
- "where were we..."
92
- "let's continue..."
93
-
94
- // Code references
95
- "that function we..."
96
- "the class that..."
97
- "the file where..."
98
70
  ```
99
-
100
- ## Configuration
101
-
102
- ```jsonc
103
- {
104
- "claudecode-rlm": {
105
- "graph": {
106
- "enabled": true, // Enable graph features
107
- "auto_ingest": true, // Auto-ingest archived context
108
- "max_traversal_nodes": 50, // Max nodes in traversal
109
- "max_traversal_depth": 3 // Max BFS depth
110
- },
111
- "enhanced_search": {
112
- "additional_patterns": true, // Use extended patterns
113
- "recency_weight": 0.3, // Recency scoring weight (0-1)
114
- "entity_boost": 0.2, // Entity match boost (0-1)
115
- "max_age_days": 30, // Max age for recency
116
- "min_score_threshold": 0.3 // Min score for results
117
- },
118
- "task_detection": {
119
- "auto_archive_on_completion": true
120
- }
121
- }
122
- }
71
+ - Keyword matching with recency weighting
72
+ - Entity-aware boosting
73
+ - Configurable scoring parameters
123
74
  ```
124
75
 
125
- ## Tools
126
-
127
76
  ### graph_query
128
77
 
129
- Query the knowledge graph directly:
78
+ Query the knowledge graph directly.
130
79
 
131
80
  ```
132
81
  Operations:
@@ -136,23 +85,81 @@ Operations:
136
85
  - path: Find connection between entities
137
86
  ```
138
87
 
139
- ### memory_enhanced
88
+ ### list_entities
89
+
90
+ List tracked entities (code elements, files, concepts).
91
+
92
+ ### graph_stats
93
+
94
+ Get knowledge graph statistics and cache performance.
95
+
96
+ ## Environment Variables
97
+
98
+ | Variable | Description | Default |
99
+ |----------|-------------|---------|
100
+ | `CLAUDECODE_RLM_WORKDIR` | Working directory for storage | Current directory |
101
+ | `CLAUDECODE_RLM_SESSION` | Default session ID | `default` |
102
+
103
+ ## Architecture
104
+
105
+ ### Knowledge Graph Structure
106
+
107
+ ```
108
+ Document (stored content block)
109
+ ├── Section (markdown header or paragraph group)
110
+ │ ├── Chunk (retrieval unit, ~300 chars)
111
+ │ │ ├── Entity (code element)
112
+ │ │ ├── Entity (file path)
113
+ │ │ └── Entity (concept)
114
+ │ └── Chunk
115
+ │ └── [FOLLOWS] → next chunk
116
+ └── Section
117
+ ```
118
+
119
+ ### Storage
140
120
 
141
- Search with enhanced scoring:
121
+ Data persists to `.claude/claudecode-rlm/graph/{sessionID}/`:
142
122
 
143
123
  ```
144
- - Keyword matching with recency weighting
145
- - Entity-aware boosting
146
- - Graph traversal for related context
124
+ nodes/*.json - Individual node files
125
+ edges/*.json - Adjacency lists per node
126
+ indexes/*.json - Type, entity, and inverted word indexes
147
127
  ```
148
128
 
149
- ### list_entities
129
+ ## Performance
150
130
 
151
- List tracked entities (code elements, files, concepts).
131
+ Benchmarks comparing basic vs optimized storage (10,000 nodes):
152
132
 
153
- ### graph_stats
133
+ | Operation | Basic | Optimized | Speedup |
134
+ |-----------|-------|-----------|---------|
135
+ | Node reads | 21.3µs | 0.3µs | **74x** |
136
+ | Get by type | 1.1ms | 21µs | **54x** |
137
+ | Bulk insert | 6.07s | 1.21s | **5x** |
138
+ | Search | 0.45ms | 0.65ms | ~1x |
154
139
 
155
- Get knowledge graph statistics and cache performance.
140
+ ## Library Usage
141
+
142
+ You can also use claudecode-rlm as a library:
143
+
144
+ ```javascript
145
+ import { GraphStorage, GraphIngester, EnhancedSearch } from 'claudecode-rlm'
146
+
147
+ // Initialize
148
+ GraphStorage.init('/path/to/project')
149
+
150
+ // Store content
151
+ GraphIngester.ingestContextBlock({
152
+ id: 'block-1',
153
+ sessionID: 'my-session',
154
+ content: 'Your conversation content...',
155
+ summary: 'Summary',
156
+ tokens: 1000,
157
+ createdAt: Date.now()
158
+ })
159
+
160
+ // Search
161
+ const results = EnhancedSearch.search('my-session', 'your query')
162
+ ```
156
163
 
157
164
  ## Development
158
165
 
@@ -160,37 +167,15 @@ Get knowledge graph statistics and cache performance.
160
167
  # Install dependencies
161
168
  npm install
162
169
 
163
- # Type check
164
- npm run typecheck
165
-
166
170
  # Build
167
171
  npm run build
168
172
 
169
- # Run benchmarks
170
- npm run test
171
- npm run test:optimized
172
- ```
173
-
174
- ## How It Works
173
+ # Run MCP server locally
174
+ npm start
175
175
 
176
- 1. **Context Archival**: When Claude Code archives context (RLM threshold reached), claudecode-rlm:
177
- - Creates a Document node for the archived block
178
- - Splits content into Sections (by headers or paragraphs)
179
- - Chunks sections into retrieval units (~300 chars with overlap)
180
- - Extracts entities (code elements, file paths, concepts)
181
- - Links everything with typed edges
182
-
183
- 2. **Context Injection**: When a user message matches reference patterns (e.g., "do you remember...", "we discussed..."):
184
- - claudecode-rlm detects the pattern via `experimental.chat.messages.transform` hook
185
- - Searches the inverted index for matching chunks
186
- - Boosts results by entity matches and recency
187
- - Expands context via graph traversal
188
- - **Injects relevant context into the message stream before the LLM sees it**
189
-
190
- 3. **Caching**: The LRU cache ensures:
191
- - Recently accessed nodes are instant to retrieve
192
- - Cache hit rate approaches 100% in typical usage
193
- - Memory usage bounded by cache size (2000 nodes)
176
+ # Type check
177
+ npm run typecheck
178
+ ```
194
179
 
195
180
  ## License
196
181
 
@@ -204,6 +189,6 @@ MIT
204
189
 
205
190
  ## Links
206
191
 
207
- - [claudecode-rlm Repository](https://github.com/tekcin/claudecode-rlm)
208
- - [Claude Code](https://github.com/tekcin/claude-code)
192
+ - [claudecode-rlm on npm](https://www.npmjs.com/package/claudecode-rlm)
193
+ - [GitHub Repository](https://github.com/tekcin/claudecode-rlm)
209
194
  - [Issues](https://github.com/tekcin/claudecode-rlm/issues)
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * claudecode-rlm MCP Server
4
+ *
5
+ * Provides knowledge graph-based context storage for Claude Code via MCP.
6
+ *
7
+ * Tools:
8
+ * - graph_query: Query the knowledge graph
9
+ * - memory_store: Store content in the knowledge graph
10
+ * - memory_search: Search stored content with enhanced scoring
11
+ * - list_entities: List tracked entities
12
+ * - graph_stats: Get graph statistics
13
+ *
14
+ * @module claudecode-rlm/mcp-server
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=mcp-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;GAaG"}
@@ -0,0 +1,393 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * claudecode-rlm MCP Server
4
+ *
5
+ * Provides knowledge graph-based context storage for Claude Code via MCP.
6
+ *
7
+ * Tools:
8
+ * - graph_query: Query the knowledge graph
9
+ * - memory_store: Store content in the knowledge graph
10
+ * - memory_search: Search stored content with enhanced scoring
11
+ * - list_entities: List tracked entities
12
+ * - graph_stats: Get graph statistics
13
+ *
14
+ * @module claudecode-rlm/mcp-server
15
+ */
16
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
17
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
18
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
19
+ import { GraphStorage, GraphIngester, GraphTraverser, GraphSearcher } from "./graph/index.js";
20
+ import { EnhancedSearch } from "./search/index.js";
21
+ import { NodeType } from "./graph/types.js";
22
+ // Initialize storage with current working directory
23
+ const workdir = process.env.CLAUDECODE_RLM_WORKDIR || process.cwd();
24
+ GraphStorage.init(workdir);
25
+ // Default session ID (can be overridden per-call)
26
+ const DEFAULT_SESSION = process.env.CLAUDECODE_RLM_SESSION || "default";
27
+ /**
28
+ * MCP Tool definitions
29
+ */
30
+ const TOOLS = [
31
+ {
32
+ name: "graph_query",
33
+ description: "Query the knowledge graph. Operations: search (keyword search), entity (find by entity name), expand (get related context), path (find connection between entities)",
34
+ inputSchema: {
35
+ type: "object",
36
+ properties: {
37
+ operation: {
38
+ type: "string",
39
+ enum: ["search", "entity", "expand", "path"],
40
+ description: "The operation to perform"
41
+ },
42
+ query: {
43
+ type: "string",
44
+ description: "Search query, entity name, or node ID depending on operation"
45
+ },
46
+ target: {
47
+ type: "string",
48
+ description: "Target entity for 'path' operation"
49
+ },
50
+ limit: {
51
+ type: "number",
52
+ description: "Maximum results to return (default: 10)"
53
+ },
54
+ session: {
55
+ type: "string",
56
+ description: "Session ID (default: 'default')"
57
+ }
58
+ },
59
+ required: ["operation", "query"]
60
+ }
61
+ },
62
+ {
63
+ name: "memory_store",
64
+ description: "Store content in the knowledge graph for later retrieval. Automatically extracts entities and creates searchable chunks.",
65
+ inputSchema: {
66
+ type: "object",
67
+ properties: {
68
+ content: {
69
+ type: "string",
70
+ description: "The content to store"
71
+ },
72
+ summary: {
73
+ type: "string",
74
+ description: "Optional summary of the content"
75
+ },
76
+ session: {
77
+ type: "string",
78
+ description: "Session ID (default: 'default')"
79
+ }
80
+ },
81
+ required: ["content"]
82
+ }
83
+ },
84
+ {
85
+ name: "memory_search",
86
+ description: "Search stored content with enhanced scoring including recency weighting and entity boosting.",
87
+ inputSchema: {
88
+ type: "object",
89
+ properties: {
90
+ query: {
91
+ type: "string",
92
+ description: "Search query"
93
+ },
94
+ limit: {
95
+ type: "number",
96
+ description: "Maximum results (default: 10)"
97
+ },
98
+ recency_weight: {
99
+ type: "number",
100
+ description: "Recency weight 0-1 (default: 0.3)"
101
+ },
102
+ entity_boost: {
103
+ type: "number",
104
+ description: "Entity match boost 0-1 (default: 0.2)"
105
+ },
106
+ session: {
107
+ type: "string",
108
+ description: "Session ID (default: 'default')"
109
+ }
110
+ },
111
+ required: ["query"]
112
+ }
113
+ },
114
+ {
115
+ name: "list_entities",
116
+ description: "List all tracked entities (code elements, files, concepts) in the knowledge graph.",
117
+ inputSchema: {
118
+ type: "object",
119
+ properties: {
120
+ filter: {
121
+ type: "string",
122
+ description: "Optional filter string to match entity names"
123
+ },
124
+ limit: {
125
+ type: "number",
126
+ description: "Maximum entities to return (default: 50)"
127
+ },
128
+ session: {
129
+ type: "string",
130
+ description: "Session ID (default: 'default')"
131
+ }
132
+ }
133
+ }
134
+ },
135
+ {
136
+ name: "graph_stats",
137
+ description: "Get knowledge graph statistics including node counts, cache performance, and storage info.",
138
+ inputSchema: {
139
+ type: "object",
140
+ properties: {
141
+ session: {
142
+ type: "string",
143
+ description: "Session ID (default: 'default')"
144
+ }
145
+ }
146
+ }
147
+ }
148
+ ];
149
+ /**
150
+ * Handle tool execution
151
+ */
152
+ async function handleToolCall(name, args) {
153
+ const session = args.session || DEFAULT_SESSION;
154
+ switch (name) {
155
+ case "graph_query": {
156
+ const operation = args.operation;
157
+ const query = args.query;
158
+ const limit = args.limit || 10;
159
+ switch (operation) {
160
+ case "search": {
161
+ const results = GraphSearcher.search(session, query, { limit });
162
+ if (results.length === 0) {
163
+ return `No results found for "${query}"`;
164
+ }
165
+ return formatSearchResults(results, query);
166
+ }
167
+ case "entity": {
168
+ const chunks = GraphTraverser.getEntityContext(session, query, limit);
169
+ if (chunks.length === 0) {
170
+ return `No content found mentioning "${query}"`;
171
+ }
172
+ return formatEntityResults(chunks, query);
173
+ }
174
+ case "expand": {
175
+ const expanded = GraphTraverser.expandContext(session, [query], 2, limit);
176
+ if (expanded.length === 0) {
177
+ return `No related context found for node "${query}"`;
178
+ }
179
+ return formatExpandResults(expanded);
180
+ }
181
+ case "path": {
182
+ const target = args.target;
183
+ if (!target) {
184
+ return "Error: 'target' is required for path operation";
185
+ }
186
+ const path = GraphTraverser.findPath(session, query, target, 5);
187
+ if (!path) {
188
+ return `No path found between "${query}" and "${target}"`;
189
+ }
190
+ return formatPathResults(path, query, target);
191
+ }
192
+ default:
193
+ return `Unknown operation: ${operation}`;
194
+ }
195
+ }
196
+ case "memory_store": {
197
+ const content = args.content;
198
+ const summary = args.summary || "";
199
+ const block = {
200
+ id: `block-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
201
+ sessionID: session,
202
+ content,
203
+ summary,
204
+ tokens: Math.ceil(content.length / 4), // rough estimate
205
+ createdAt: Date.now()
206
+ };
207
+ const docNode = GraphIngester.ingestContextBlock(block);
208
+ GraphStorage.flush();
209
+ const stats = GraphStorage.getStats(session);
210
+ return `Stored content successfully.\n\nDocument ID: ${docNode.id}\nChunks created: ${stats.nodesByType["chunk"] || 0}\nEntities extracted: ${stats.nodesByType["entity"] || 0}`;
211
+ }
212
+ case "memory_search": {
213
+ const query = args.query;
214
+ const limit = args.limit || 10;
215
+ const recencyWeight = args.recency_weight || 0.3;
216
+ const entityBoost = args.entity_boost || 0.2;
217
+ const results = EnhancedSearch.search(session, query, {
218
+ limit,
219
+ recencyWeight,
220
+ entityBoost,
221
+ minScoreThreshold: 0.1
222
+ });
223
+ if (results.length === 0) {
224
+ return `No results found for "${query}"`;
225
+ }
226
+ return formatEnhancedResults(results, query);
227
+ }
228
+ case "list_entities": {
229
+ const filter = args.filter;
230
+ const limit = args.limit || 50;
231
+ const entities = GraphStorage.getNodesByType(session, NodeType.ENTITY, limit);
232
+ let filtered = entities;
233
+ if (filter) {
234
+ const filterLower = filter.toLowerCase();
235
+ filtered = entities.filter(e => e.content.toLowerCase().includes(filterLower));
236
+ }
237
+ if (filtered.length === 0) {
238
+ return filter
239
+ ? `No entities found matching "${filter}"`
240
+ : "No entities tracked yet. Store some content first.";
241
+ }
242
+ const grouped = {};
243
+ for (const entity of filtered) {
244
+ const type = entity.metadata?.entityType || "unknown";
245
+ if (!grouped[type])
246
+ grouped[type] = [];
247
+ grouped[type].push(entity.content);
248
+ }
249
+ let output = `Found ${filtered.length} entities:\n\n`;
250
+ for (const [type, names] of Object.entries(grouped)) {
251
+ output += `**${type}** (${names.length}):\n`;
252
+ output += names.slice(0, 20).map(n => ` - ${n}`).join("\n");
253
+ if (names.length > 20)
254
+ output += `\n ... and ${names.length - 20} more`;
255
+ output += "\n\n";
256
+ }
257
+ return output.trim();
258
+ }
259
+ case "graph_stats": {
260
+ const stats = GraphStorage.getStats(session);
261
+ const sessions = GraphStorage.getSessions();
262
+ return `**Knowledge Graph Statistics**
263
+
264
+ Session: ${session}
265
+ Total Sessions: ${sessions.length}
266
+
267
+ **Nodes:**
268
+ - Documents: ${stats.nodesByType["document"] || 0}
269
+ - Sections: ${stats.nodesByType["section"] || 0}
270
+ - Chunks: ${stats.nodesByType["chunk"] || 0}
271
+ - Entities: ${stats.nodesByType["entity"] || 0}
272
+ - Total: ${stats.nodeCount}
273
+
274
+ **Edges:** ${stats.edgeCount}
275
+
276
+ **Cache Performance:**
277
+ - Hits: ${stats.cacheStats.hits}
278
+ - Misses: ${stats.cacheStats.misses}
279
+ - Hit Rate: ${stats.cacheStats.hitRate}
280
+
281
+ **Storage:** .claude/claudecode-rlm/graph/${session}/`;
282
+ }
283
+ default:
284
+ return `Unknown tool: ${name}`;
285
+ }
286
+ }
287
+ /**
288
+ * Format search results
289
+ */
290
+ function formatSearchResults(results, query) {
291
+ let output = `Found ${results.length} results for "${query}":\n\n`;
292
+ for (let i = 0; i < results.length; i++) {
293
+ const { node, score } = results[i];
294
+ const date = new Date(node.createdAt).toLocaleDateString();
295
+ const preview = node.content.slice(0, 200).replace(/\n/g, " ");
296
+ output += `**[${i + 1}]** (score: ${score.toFixed(2)}, ${date})\n${preview}${node.content.length > 200 ? "..." : ""}\n\n`;
297
+ }
298
+ return output.trim();
299
+ }
300
+ /**
301
+ * Format entity results
302
+ */
303
+ function formatEntityResults(chunks, entity) {
304
+ let output = `Found ${chunks.length} mentions of "${entity}":\n\n`;
305
+ for (let i = 0; i < chunks.length; i++) {
306
+ const chunk = chunks[i];
307
+ const date = new Date(chunk.createdAt).toLocaleDateString();
308
+ const preview = chunk.content.slice(0, 200).replace(/\n/g, " ");
309
+ output += `**[${i + 1}]** (${date})\n${preview}${chunk.content.length > 200 ? "..." : ""}\n\n`;
310
+ }
311
+ return output.trim();
312
+ }
313
+ /**
314
+ * Format expand results
315
+ */
316
+ function formatExpandResults(nodes) {
317
+ let output = `Related context (${nodes.length} nodes):\n\n`;
318
+ for (const node of nodes) {
319
+ const preview = node.content.slice(0, 150).replace(/\n/g, " ");
320
+ output += `- [${node.type}] ${preview}${node.content.length > 150 ? "..." : ""}\n`;
321
+ }
322
+ return output.trim();
323
+ }
324
+ /**
325
+ * Format path results
326
+ */
327
+ function formatPathResults(path, start, end) {
328
+ let output = `Path from "${start}" to "${end}" (${path.length} nodes):\n\n`;
329
+ for (let i = 0; i < path.length; i++) {
330
+ const node = path[i];
331
+ const preview = node.content.slice(0, 100).replace(/\n/g, " ");
332
+ output += `${i + 1}. [${node.type}] ${preview}\n`;
333
+ if (i < path.length - 1)
334
+ output += " ↓\n";
335
+ }
336
+ return output.trim();
337
+ }
338
+ /**
339
+ * Format enhanced search results
340
+ */
341
+ function formatEnhancedResults(results, query) {
342
+ let output = `Found ${results.length} results for "${query}":\n\n`;
343
+ for (let i = 0; i < results.length; i++) {
344
+ const { node, finalScore, recencyScore } = results[i];
345
+ const date = new Date(node.createdAt).toLocaleDateString();
346
+ const preview = node.content.slice(0, 200).replace(/\n/g, " ");
347
+ output += `**[${i + 1}]** (score: ${finalScore.toFixed(2)}, recency: ${recencyScore.toFixed(2)}, ${date})\n${preview}${node.content.length > 200 ? "..." : ""}\n\n`;
348
+ }
349
+ return output.trim();
350
+ }
351
+ /**
352
+ * Main MCP server
353
+ */
354
+ async function main() {
355
+ const server = new Server({
356
+ name: "claudecode-rlm",
357
+ version: "1.0.0"
358
+ }, {
359
+ capabilities: {
360
+ tools: {}
361
+ }
362
+ });
363
+ // List available tools
364
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
365
+ return { tools: TOOLS };
366
+ });
367
+ // Handle tool calls
368
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
369
+ const { name, arguments: args } = request.params;
370
+ try {
371
+ const result = await handleToolCall(name, args || {});
372
+ return {
373
+ content: [{ type: "text", text: result }]
374
+ };
375
+ }
376
+ catch (error) {
377
+ const message = error instanceof Error ? error.message : String(error);
378
+ return {
379
+ content: [{ type: "text", text: `Error: ${message}` }],
380
+ isError: true
381
+ };
382
+ }
383
+ });
384
+ // Start server
385
+ const transport = new StdioServerTransport();
386
+ await server.connect(transport);
387
+ console.error("[claudecode-rlm] MCP server started");
388
+ }
389
+ main().catch((error) => {
390
+ console.error("[claudecode-rlm] Fatal error:", error);
391
+ process.exit(1);
392
+ });
393
+ //# sourceMappingURL=mcp-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "claudecode-rlm",
4
- "version": "1.0.0",
5
- "description": "Advanced RLM (Recursive Language Model) plugin for Claude Code - Knowledge graph-based context storage with 74x faster reads",
4
+ "version": "1.1.0",
5
+ "description": "MCP server for Claude Code - Knowledge graph-based context storage with 74x faster reads",
6
6
  "type": "module",
7
7
  "license": "MIT",
8
8
  "author": "Michael Thornton <tekcin@yahoo.com>",
@@ -15,22 +15,29 @@
15
15
  "url": "https://github.com/tekcin/claudecode-rlm/issues"
16
16
  },
17
17
  "keywords": [
18
+ "mcp",
19
+ "mcp-server",
18
20
  "claude-code",
19
- "claudecode",
20
- "plugin",
21
- "rlm",
22
- "context",
21
+ "claude",
22
+ "anthropic",
23
23
  "knowledge-graph",
24
+ "context",
25
+ "memory",
24
26
  "ai",
25
- "llm",
26
- "anthropic"
27
+ "llm"
27
28
  ],
28
29
  "main": "./dist/index.js",
29
30
  "types": "./dist/index.d.ts",
31
+ "bin": {
32
+ "claudecode-rlm": "./dist/mcp-server.js"
33
+ },
30
34
  "exports": {
31
35
  ".": {
32
36
  "import": "./dist/index.js",
33
37
  "types": "./dist/index.d.ts"
38
+ },
39
+ "./mcp-server": {
40
+ "import": "./dist/mcp-server.js"
34
41
  }
35
42
  },
36
43
  "files": [
@@ -41,21 +48,15 @@
41
48
  "build": "tsc",
42
49
  "dev": "tsc --watch",
43
50
  "typecheck": "tsc --noEmit",
51
+ "start": "node dist/mcp-server.js",
44
52
  "test": "node test/benchmark.cjs",
45
53
  "test:optimized": "node test/benchmark-optimized.cjs",
46
54
  "prepublishOnly": "npm run build"
47
55
  },
48
56
  "dependencies": {
57
+ "@modelcontextprotocol/sdk": "^1.25.3",
49
58
  "zod": "^3.23.0"
50
59
  },
51
- "peerDependencies": {
52
- "@anthropic-ai/claude-code": ">=1.0.0"
53
- },
54
- "peerDependenciesMeta": {
55
- "@anthropic-ai/claude-code": {
56
- "optional": true
57
- }
58
- },
59
60
  "devDependencies": {
60
61
  "@types/node": "^22.0.0",
61
62
  "typescript": "^5.6.0"
@@ -0,0 +1,443 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * claudecode-rlm MCP Server
4
+ *
5
+ * Provides knowledge graph-based context storage for Claude Code via MCP.
6
+ *
7
+ * Tools:
8
+ * - graph_query: Query the knowledge graph
9
+ * - memory_store: Store content in the knowledge graph
10
+ * - memory_search: Search stored content with enhanced scoring
11
+ * - list_entities: List tracked entities
12
+ * - graph_stats: Get graph statistics
13
+ *
14
+ * @module claudecode-rlm/mcp-server
15
+ */
16
+
17
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js"
18
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
19
+ import {
20
+ CallToolRequestSchema,
21
+ ListToolsRequestSchema,
22
+ type Tool,
23
+ } from "@modelcontextprotocol/sdk/types.js"
24
+
25
+ import { GraphStorage, GraphIngester, GraphTraverser, GraphSearcher } from "./graph/index.js"
26
+ import { EnhancedSearch, detectsReference } from "./search/index.js"
27
+ import { NodeType } from "./graph/types.js"
28
+
29
+ // Initialize storage with current working directory
30
+ const workdir = process.env.CLAUDECODE_RLM_WORKDIR || process.cwd()
31
+ GraphStorage.init(workdir)
32
+
33
+ // Default session ID (can be overridden per-call)
34
+ const DEFAULT_SESSION = process.env.CLAUDECODE_RLM_SESSION || "default"
35
+
36
+ /**
37
+ * MCP Tool definitions
38
+ */
39
+ const TOOLS: Tool[] = [
40
+ {
41
+ name: "graph_query",
42
+ description: "Query the knowledge graph. Operations: search (keyword search), entity (find by entity name), expand (get related context), path (find connection between entities)",
43
+ inputSchema: {
44
+ type: "object" as const,
45
+ properties: {
46
+ operation: {
47
+ type: "string",
48
+ enum: ["search", "entity", "expand", "path"],
49
+ description: "The operation to perform"
50
+ },
51
+ query: {
52
+ type: "string",
53
+ description: "Search query, entity name, or node ID depending on operation"
54
+ },
55
+ target: {
56
+ type: "string",
57
+ description: "Target entity for 'path' operation"
58
+ },
59
+ limit: {
60
+ type: "number",
61
+ description: "Maximum results to return (default: 10)"
62
+ },
63
+ session: {
64
+ type: "string",
65
+ description: "Session ID (default: 'default')"
66
+ }
67
+ },
68
+ required: ["operation", "query"]
69
+ }
70
+ },
71
+ {
72
+ name: "memory_store",
73
+ description: "Store content in the knowledge graph for later retrieval. Automatically extracts entities and creates searchable chunks.",
74
+ inputSchema: {
75
+ type: "object" as const,
76
+ properties: {
77
+ content: {
78
+ type: "string",
79
+ description: "The content to store"
80
+ },
81
+ summary: {
82
+ type: "string",
83
+ description: "Optional summary of the content"
84
+ },
85
+ session: {
86
+ type: "string",
87
+ description: "Session ID (default: 'default')"
88
+ }
89
+ },
90
+ required: ["content"]
91
+ }
92
+ },
93
+ {
94
+ name: "memory_search",
95
+ description: "Search stored content with enhanced scoring including recency weighting and entity boosting.",
96
+ inputSchema: {
97
+ type: "object" as const,
98
+ properties: {
99
+ query: {
100
+ type: "string",
101
+ description: "Search query"
102
+ },
103
+ limit: {
104
+ type: "number",
105
+ description: "Maximum results (default: 10)"
106
+ },
107
+ recency_weight: {
108
+ type: "number",
109
+ description: "Recency weight 0-1 (default: 0.3)"
110
+ },
111
+ entity_boost: {
112
+ type: "number",
113
+ description: "Entity match boost 0-1 (default: 0.2)"
114
+ },
115
+ session: {
116
+ type: "string",
117
+ description: "Session ID (default: 'default')"
118
+ }
119
+ },
120
+ required: ["query"]
121
+ }
122
+ },
123
+ {
124
+ name: "list_entities",
125
+ description: "List all tracked entities (code elements, files, concepts) in the knowledge graph.",
126
+ inputSchema: {
127
+ type: "object" as const,
128
+ properties: {
129
+ filter: {
130
+ type: "string",
131
+ description: "Optional filter string to match entity names"
132
+ },
133
+ limit: {
134
+ type: "number",
135
+ description: "Maximum entities to return (default: 50)"
136
+ },
137
+ session: {
138
+ type: "string",
139
+ description: "Session ID (default: 'default')"
140
+ }
141
+ }
142
+ }
143
+ },
144
+ {
145
+ name: "graph_stats",
146
+ description: "Get knowledge graph statistics including node counts, cache performance, and storage info.",
147
+ inputSchema: {
148
+ type: "object" as const,
149
+ properties: {
150
+ session: {
151
+ type: "string",
152
+ description: "Session ID (default: 'default')"
153
+ }
154
+ }
155
+ }
156
+ }
157
+ ]
158
+
159
+ /**
160
+ * Handle tool execution
161
+ */
162
+ async function handleToolCall(name: string, args: Record<string, unknown>): Promise<string> {
163
+ const session = (args.session as string) || DEFAULT_SESSION
164
+
165
+ switch (name) {
166
+ case "graph_query": {
167
+ const operation = args.operation as string
168
+ const query = args.query as string
169
+ const limit = (args.limit as number) || 10
170
+
171
+ switch (operation) {
172
+ case "search": {
173
+ const results = GraphSearcher.search(session, query, { limit })
174
+ if (results.length === 0) {
175
+ return `No results found for "${query}"`
176
+ }
177
+ return formatSearchResults(results, query)
178
+ }
179
+ case "entity": {
180
+ const chunks = GraphTraverser.getEntityContext(session, query, limit)
181
+ if (chunks.length === 0) {
182
+ return `No content found mentioning "${query}"`
183
+ }
184
+ return formatEntityResults(chunks, query)
185
+ }
186
+ case "expand": {
187
+ const expanded = GraphTraverser.expandContext(session, [query], 2, limit)
188
+ if (expanded.length === 0) {
189
+ return `No related context found for node "${query}"`
190
+ }
191
+ return formatExpandResults(expanded)
192
+ }
193
+ case "path": {
194
+ const target = args.target as string
195
+ if (!target) {
196
+ return "Error: 'target' is required for path operation"
197
+ }
198
+ const path = GraphTraverser.findPath(session, query, target, 5)
199
+ if (!path) {
200
+ return `No path found between "${query}" and "${target}"`
201
+ }
202
+ return formatPathResults(path, query, target)
203
+ }
204
+ default:
205
+ return `Unknown operation: ${operation}`
206
+ }
207
+ }
208
+
209
+ case "memory_store": {
210
+ const content = args.content as string
211
+ const summary = (args.summary as string) || ""
212
+
213
+ const block = {
214
+ id: `block-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
215
+ sessionID: session,
216
+ content,
217
+ summary,
218
+ tokens: Math.ceil(content.length / 4), // rough estimate
219
+ createdAt: Date.now()
220
+ }
221
+
222
+ const docNode = GraphIngester.ingestContextBlock(block)
223
+ GraphStorage.flush()
224
+
225
+ const stats = GraphStorage.getStats(session)
226
+ return `Stored content successfully.\n\nDocument ID: ${docNode.id}\nChunks created: ${stats.nodesByType["chunk"] || 0}\nEntities extracted: ${stats.nodesByType["entity"] || 0}`
227
+ }
228
+
229
+ case "memory_search": {
230
+ const query = args.query as string
231
+ const limit = (args.limit as number) || 10
232
+ const recencyWeight = (args.recency_weight as number) || 0.3
233
+ const entityBoost = (args.entity_boost as number) || 0.2
234
+
235
+ const results = EnhancedSearch.search(session, query, {
236
+ limit,
237
+ recencyWeight,
238
+ entityBoost,
239
+ minScoreThreshold: 0.1
240
+ })
241
+
242
+ if (results.length === 0) {
243
+ return `No results found for "${query}"`
244
+ }
245
+
246
+ return formatEnhancedResults(results, query)
247
+ }
248
+
249
+ case "list_entities": {
250
+ const filter = args.filter as string | undefined
251
+ const limit = (args.limit as number) || 50
252
+
253
+ const entities = GraphStorage.getNodesByType(session, NodeType.ENTITY, limit)
254
+
255
+ let filtered = entities
256
+ if (filter) {
257
+ const filterLower = filter.toLowerCase()
258
+ filtered = entities.filter(e => e.content.toLowerCase().includes(filterLower))
259
+ }
260
+
261
+ if (filtered.length === 0) {
262
+ return filter
263
+ ? `No entities found matching "${filter}"`
264
+ : "No entities tracked yet. Store some content first."
265
+ }
266
+
267
+ const grouped: Record<string, string[]> = {}
268
+ for (const entity of filtered) {
269
+ const type = (entity.metadata?.entityType as string) || "unknown"
270
+ if (!grouped[type]) grouped[type] = []
271
+ grouped[type].push(entity.content)
272
+ }
273
+
274
+ let output = `Found ${filtered.length} entities:\n\n`
275
+ for (const [type, names] of Object.entries(grouped)) {
276
+ output += `**${type}** (${names.length}):\n`
277
+ output += names.slice(0, 20).map(n => ` - ${n}`).join("\n")
278
+ if (names.length > 20) output += `\n ... and ${names.length - 20} more`
279
+ output += "\n\n"
280
+ }
281
+
282
+ return output.trim()
283
+ }
284
+
285
+ case "graph_stats": {
286
+ const stats = GraphStorage.getStats(session)
287
+ const sessions = GraphStorage.getSessions()
288
+
289
+ return `**Knowledge Graph Statistics**
290
+
291
+ Session: ${session}
292
+ Total Sessions: ${sessions.length}
293
+
294
+ **Nodes:**
295
+ - Documents: ${stats.nodesByType["document"] || 0}
296
+ - Sections: ${stats.nodesByType["section"] || 0}
297
+ - Chunks: ${stats.nodesByType["chunk"] || 0}
298
+ - Entities: ${stats.nodesByType["entity"] || 0}
299
+ - Total: ${stats.nodeCount}
300
+
301
+ **Edges:** ${stats.edgeCount}
302
+
303
+ **Cache Performance:**
304
+ - Hits: ${stats.cacheStats.hits}
305
+ - Misses: ${stats.cacheStats.misses}
306
+ - Hit Rate: ${stats.cacheStats.hitRate}
307
+
308
+ **Storage:** .claude/claudecode-rlm/graph/${session}/`
309
+ }
310
+
311
+ default:
312
+ return `Unknown tool: ${name}`
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Format search results
318
+ */
319
+ function formatSearchResults(results: Array<{ node: { content: string; createdAt: number }; score: number }>, query: string): string {
320
+ let output = `Found ${results.length} results for "${query}":\n\n`
321
+
322
+ for (let i = 0; i < results.length; i++) {
323
+ const { node, score } = results[i]
324
+ const date = new Date(node.createdAt).toLocaleDateString()
325
+ const preview = node.content.slice(0, 200).replace(/\n/g, " ")
326
+ output += `**[${i + 1}]** (score: ${score.toFixed(2)}, ${date})\n${preview}${node.content.length > 200 ? "..." : ""}\n\n`
327
+ }
328
+
329
+ return output.trim()
330
+ }
331
+
332
+ /**
333
+ * Format entity results
334
+ */
335
+ function formatEntityResults(chunks: Array<{ content: string; createdAt: number }>, entity: string): string {
336
+ let output = `Found ${chunks.length} mentions of "${entity}":\n\n`
337
+
338
+ for (let i = 0; i < chunks.length; i++) {
339
+ const chunk = chunks[i]
340
+ const date = new Date(chunk.createdAt).toLocaleDateString()
341
+ const preview = chunk.content.slice(0, 200).replace(/\n/g, " ")
342
+ output += `**[${i + 1}]** (${date})\n${preview}${chunk.content.length > 200 ? "..." : ""}\n\n`
343
+ }
344
+
345
+ return output.trim()
346
+ }
347
+
348
+ /**
349
+ * Format expand results
350
+ */
351
+ function formatExpandResults(nodes: Array<{ type: string; content: string }>): string {
352
+ let output = `Related context (${nodes.length} nodes):\n\n`
353
+
354
+ for (const node of nodes) {
355
+ const preview = node.content.slice(0, 150).replace(/\n/g, " ")
356
+ output += `- [${node.type}] ${preview}${node.content.length > 150 ? "..." : ""}\n`
357
+ }
358
+
359
+ return output.trim()
360
+ }
361
+
362
+ /**
363
+ * Format path results
364
+ */
365
+ function formatPathResults(path: Array<{ type: string; content: string }>, start: string, end: string): string {
366
+ let output = `Path from "${start}" to "${end}" (${path.length} nodes):\n\n`
367
+
368
+ for (let i = 0; i < path.length; i++) {
369
+ const node = path[i]
370
+ const preview = node.content.slice(0, 100).replace(/\n/g, " ")
371
+ output += `${i + 1}. [${node.type}] ${preview}\n`
372
+ if (i < path.length - 1) output += " ↓\n"
373
+ }
374
+
375
+ return output.trim()
376
+ }
377
+
378
+ /**
379
+ * Format enhanced search results
380
+ */
381
+ function formatEnhancedResults(results: Array<{ node: { content: string; createdAt: number }; finalScore: number; recencyScore: number }>, query: string): string {
382
+ let output = `Found ${results.length} results for "${query}":\n\n`
383
+
384
+ for (let i = 0; i < results.length; i++) {
385
+ const { node, finalScore, recencyScore } = results[i]
386
+ const date = new Date(node.createdAt).toLocaleDateString()
387
+ const preview = node.content.slice(0, 200).replace(/\n/g, " ")
388
+ output += `**[${i + 1}]** (score: ${finalScore.toFixed(2)}, recency: ${recencyScore.toFixed(2)}, ${date})\n${preview}${node.content.length > 200 ? "..." : ""}\n\n`
389
+ }
390
+
391
+ return output.trim()
392
+ }
393
+
394
+ /**
395
+ * Main MCP server
396
+ */
397
+ async function main() {
398
+ const server = new Server(
399
+ {
400
+ name: "claudecode-rlm",
401
+ version: "1.0.0"
402
+ },
403
+ {
404
+ capabilities: {
405
+ tools: {}
406
+ }
407
+ }
408
+ )
409
+
410
+ // List available tools
411
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
412
+ return { tools: TOOLS }
413
+ })
414
+
415
+ // Handle tool calls
416
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
417
+ const { name, arguments: args } = request.params
418
+
419
+ try {
420
+ const result = await handleToolCall(name, args as Record<string, unknown> || {})
421
+ return {
422
+ content: [{ type: "text", text: result }]
423
+ }
424
+ } catch (error) {
425
+ const message = error instanceof Error ? error.message : String(error)
426
+ return {
427
+ content: [{ type: "text", text: `Error: ${message}` }],
428
+ isError: true
429
+ }
430
+ }
431
+ })
432
+
433
+ // Start server
434
+ const transport = new StdioServerTransport()
435
+ await server.connect(transport)
436
+
437
+ console.error("[claudecode-rlm] MCP server started")
438
+ }
439
+
440
+ main().catch((error) => {
441
+ console.error("[claudecode-rlm] Fatal error:", error)
442
+ process.exit(1)
443
+ })