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 +113 -128
- package/dist/mcp-server.d.ts +17 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +393 -0
- package/dist/mcp-server.js.map +1 -0
- package/package.json +17 -16
- package/src/mcp-server.ts +443 -0
package/README.md
CHANGED
|
@@ -1,132 +1,81 @@
|
|
|
1
1
|
# claudecode-rlm
|
|
2
2
|
|
|
3
|
-
**
|
|
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
|
-
- **
|
|
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
|
-
- **
|
|
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
|
-
|
|
22
|
+
## Usage with Claude Code
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
Add to your Claude Code MCP settings (`~/.claude/claude_desktop_config.json`):
|
|
25
|
+
|
|
26
|
+
```json
|
|
28
27
|
{
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
Benchmarks comparing basic vs optimized storage (10,000 nodes):
|
|
39
|
+
Or with npx (no global install):
|
|
47
40
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
55
|
+
## Available Tools
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
### memory_store
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
Store content in the knowledge graph for later retrieval.
|
|
60
60
|
|
|
61
61
|
```
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
121
|
+
Data persists to `.claude/claudecode-rlm/graph/{sessionID}/`:
|
|
142
122
|
|
|
143
123
|
```
|
|
144
|
-
-
|
|
145
|
-
-
|
|
146
|
-
-
|
|
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
|
-
|
|
129
|
+
## Performance
|
|
150
130
|
|
|
151
|
-
|
|
131
|
+
Benchmarks comparing basic vs optimized storage (10,000 nodes):
|
|
152
132
|
|
|
153
|
-
|
|
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
|
-
|
|
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
|
|
170
|
-
npm
|
|
171
|
-
npm run test:optimized
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
## How It Works
|
|
173
|
+
# Run MCP server locally
|
|
174
|
+
npm start
|
|
175
175
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
|
208
|
-
- [
|
|
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.
|
|
5
|
-
"description": "
|
|
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
|
-
"
|
|
20
|
-
"
|
|
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
|
+
})
|