claude-recall 0.2.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/LICENSE +21 -0
- package/README.md +211 -0
- package/dist/cli/claude-recall-cli.js +345 -0
- package/dist/cli/commands/migrate.js +245 -0
- package/dist/core/pattern-detector.js +242 -0
- package/dist/core/patterns.js +56 -0
- package/dist/core/retrieval.js +108 -0
- package/dist/mcp/rate-limiter.js +114 -0
- package/dist/mcp/server.js +283 -0
- package/dist/mcp/session-manager.js +161 -0
- package/dist/mcp/tools/memory-tools.js +358 -0
- package/dist/mcp/transports/stdio.js +246 -0
- package/dist/memory/pattern-store.js +66 -0
- package/dist/memory/schema.sql +24 -0
- package/dist/memory/storage.js +274 -0
- package/dist/services/action-pattern-detector.js +251 -0
- package/dist/services/claude-json-watcher.js +243 -0
- package/dist/services/config.js +149 -0
- package/dist/services/database-manager.js +332 -0
- package/dist/services/logging.js +124 -0
- package/dist/services/memory-enhancer.js +148 -0
- package/dist/services/memory.js +334 -0
- package/dist/services/pattern-service.js +172 -0
- package/dist/services/preference-extractor.js +286 -0
- package/dist/services/semantic-preference-extractor.js +432 -0
- package/docs/MCP_API_REFERENCE.md +257 -0
- package/docs/MCP_USER_GUIDE.md +115 -0
- package/docs/release-process.md +86 -0
- package/package.json +89 -0
- package/scripts/postinstall.js +76 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SemanticPreferenceExtractor = void 0;
|
|
4
|
+
const logging_1 = require("./logging");
|
|
5
|
+
class SemanticPreferenceExtractor {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.logger = logging_1.LoggingService.getInstance();
|
|
8
|
+
// Semantic patterns for intent identification
|
|
9
|
+
this.INTENT_PATTERNS = [
|
|
10
|
+
// Location preferences - very flexible patterns
|
|
11
|
+
{
|
|
12
|
+
pattern: /(?:let'?s?\s+)?(?:put|save|store|place|keep|create)\s+(.+?)\s+(?:in|into|at|under|within)\s+([^\s].+?)(?:\s+from\s+now\s+on|\s+going\s+forward|\s+moving\s+forward)?$/i,
|
|
13
|
+
category: 'location',
|
|
14
|
+
extractor: (match) => {
|
|
15
|
+
let location = match[2].trim();
|
|
16
|
+
// Remove trailing temporal phrases
|
|
17
|
+
location = location.replace(/\s+(from\s+now\s+on|going\s+forward|moving\s+forward)$/i, '');
|
|
18
|
+
return {
|
|
19
|
+
action: 'save_location',
|
|
20
|
+
entities: [match[1].trim(), location]
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
pattern: /(.+?)\s+(?:should\s+)?(?:go|goes?|belong|live)(?:es)?\s+(?:in|into|at|under)\s+([^\s].+?)(?:\s+going\s+forward)?$/i,
|
|
26
|
+
category: 'location',
|
|
27
|
+
extractor: (match) => {
|
|
28
|
+
let location = match[2].trim();
|
|
29
|
+
// Remove trailing temporal phrases
|
|
30
|
+
location = location.replace(/\s+(going\s+forward|from\s+now\s+on)$/i, '');
|
|
31
|
+
return {
|
|
32
|
+
action: 'location_preference',
|
|
33
|
+
entities: [match[1].trim(), location]
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
pattern: /(?:i\s+)?(?:think|believe|want)\s+(.+?)\s+(?:should\s+)?(?:be|go)\s+(?:in|at)\s+(.+)/i,
|
|
39
|
+
category: 'location',
|
|
40
|
+
extractor: (match) => ({
|
|
41
|
+
action: 'location_opinion',
|
|
42
|
+
entities: [match[1].trim(), match[2].trim()]
|
|
43
|
+
})
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
pattern: /(?:actually|you\s+know\s+what),?\s*(?:let'?s?\s+)?(?:use|put|save)\s+(.+?)\s+(?:in|for)\s+(.+)/i,
|
|
47
|
+
category: 'location',
|
|
48
|
+
extractor: (match) => ({
|
|
49
|
+
action: 'location_change',
|
|
50
|
+
entities: [match[1].trim(), match[2].trim()]
|
|
51
|
+
})
|
|
52
|
+
},
|
|
53
|
+
// More flexible patterns for casual language
|
|
54
|
+
{
|
|
55
|
+
pattern: /(?:oh\s+)?(?:btw|by\s+the\s+way)?,?\s*(.+?)\s+should\s+(?:be|go)\s+in\s+([^\s].+?)(?:\s+moving\s+forward)?$/i,
|
|
56
|
+
category: 'location',
|
|
57
|
+
extractor: (match) => {
|
|
58
|
+
let location = match[2].trim();
|
|
59
|
+
location = location.replace(/\s+(moving\s+forward|from\s+now\s+on)$/i, '');
|
|
60
|
+
return {
|
|
61
|
+
action: 'location_preference',
|
|
62
|
+
entities: [match[1].trim(), location]
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
pattern: /(?:let'?s?\s+)?(?:start\s+)?(?:putting|placing)\s+(?:all\s+)?(.+?)\s+in\s+([^\s].+)/i,
|
|
68
|
+
category: 'location',
|
|
69
|
+
extractor: (match) => ({
|
|
70
|
+
action: 'save_location',
|
|
71
|
+
entities: [match[1].trim(), match[2].trim()]
|
|
72
|
+
})
|
|
73
|
+
},
|
|
74
|
+
// Tool preferences
|
|
75
|
+
{
|
|
76
|
+
pattern: /(?:let'?s?\s+)?use\s+(.+?)\s+(?:for|to|when)\s+(.+)/i,
|
|
77
|
+
category: 'tool',
|
|
78
|
+
extractor: (match) => ({
|
|
79
|
+
action: 'tool_preference',
|
|
80
|
+
entities: [match[1].trim(), match[2].trim()]
|
|
81
|
+
})
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
pattern: /prefer\s+(.+?)\s+(?:over|instead\s+of|rather\s+than)\s+(.+)/i,
|
|
85
|
+
category: 'tool',
|
|
86
|
+
extractor: (match) => ({
|
|
87
|
+
action: 'tool_comparison',
|
|
88
|
+
entities: [match[1].trim(), match[2].trim()]
|
|
89
|
+
})
|
|
90
|
+
},
|
|
91
|
+
// Style preferences
|
|
92
|
+
{
|
|
93
|
+
pattern: /(?:use\s+)?(\d+)\s*spaces?\s+(?:for\s+)?(?:indent|indentation)/i,
|
|
94
|
+
category: 'style',
|
|
95
|
+
extractor: (match) => ({
|
|
96
|
+
action: 'indentation_style',
|
|
97
|
+
entities: [`${match[1]}_spaces`]
|
|
98
|
+
})
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
pattern: /(?:use\s+)?tabs?\s+(?:for\s+)?(?:indent|indentation)/i,
|
|
102
|
+
category: 'style',
|
|
103
|
+
extractor: () => ({
|
|
104
|
+
action: 'indentation_style',
|
|
105
|
+
entities: ['tabs']
|
|
106
|
+
})
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
pattern: /(?:actually,?\s+)?(?:let'?s?\s+)?use\s+tabs\s+(?:instead|now)?/i,
|
|
110
|
+
category: 'style',
|
|
111
|
+
extractor: () => ({
|
|
112
|
+
action: 'indentation_style',
|
|
113
|
+
entities: ['tabs']
|
|
114
|
+
})
|
|
115
|
+
},
|
|
116
|
+
// Process preferences
|
|
117
|
+
{
|
|
118
|
+
pattern: /(?:always|never)\s+(.+)/i,
|
|
119
|
+
category: 'process',
|
|
120
|
+
extractor: (match, text) => ({
|
|
121
|
+
action: text.toLowerCase().includes('always') ? 'always_do' : 'never_do',
|
|
122
|
+
entities: [match[1].trim()]
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
];
|
|
126
|
+
// Context indicators that boost confidence
|
|
127
|
+
this.CONTEXT_BOOSTERS = {
|
|
128
|
+
temporal: ['from now on', 'going forward', 'moving forward', 'henceforth', 'starting now'],
|
|
129
|
+
change: ['actually', 'instead', 'rather', 'changed my mind', 'on second thought'],
|
|
130
|
+
emphasis: ['definitely', 'absolutely', 'really', 'certainly', 'please'],
|
|
131
|
+
casual: ['hey', 'oh', 'btw', 'by the way', 'fyi']
|
|
132
|
+
};
|
|
133
|
+
// Word variations and synonyms for entity normalization
|
|
134
|
+
this.ENTITY_SYNONYMS = {
|
|
135
|
+
'tests': ['test', 'testing', 'specs', 'spec', 'test files', 'test file'],
|
|
136
|
+
'config': ['configuration', 'configs', 'settings', 'conf'],
|
|
137
|
+
'docs': ['documentation', 'documents', 'doc'],
|
|
138
|
+
'src': ['source', 'sources', 'code'],
|
|
139
|
+
'lib': ['library', 'libraries', 'libs'],
|
|
140
|
+
'dist': ['distribution', 'build', 'output', 'out']
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Extract preferences using semantic understanding
|
|
145
|
+
*/
|
|
146
|
+
extractPreference(prompt, context) {
|
|
147
|
+
try {
|
|
148
|
+
// Step 1: Identify intent
|
|
149
|
+
const intent = this.identifyIntent(prompt);
|
|
150
|
+
if (!intent)
|
|
151
|
+
return null;
|
|
152
|
+
// Step 2: Extract components based on intent
|
|
153
|
+
const components = this.extractComponents(prompt, intent);
|
|
154
|
+
if (!components)
|
|
155
|
+
return null;
|
|
156
|
+
// Step 3: Build structured preference
|
|
157
|
+
return this.buildPreference(intent, components, prompt);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
this.logger.logServiceError('SemanticPreferenceExtractor', 'extractPreference', error);
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Extract multiple preferences from text
|
|
166
|
+
*/
|
|
167
|
+
extractAllPreferences(text) {
|
|
168
|
+
const preferences = [];
|
|
169
|
+
const sentences = this.splitIntoMeaningfulChunks(text);
|
|
170
|
+
for (const sentence of sentences) {
|
|
171
|
+
const preference = this.extractPreference(sentence);
|
|
172
|
+
if (preference && preference.confidence > 0.5) {
|
|
173
|
+
preferences.push(preference);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return preferences;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Identify the intent category and extract basic information
|
|
180
|
+
*/
|
|
181
|
+
identifyIntent(prompt) {
|
|
182
|
+
let bestMatch = null;
|
|
183
|
+
let highestConfidence = 0;
|
|
184
|
+
for (const pattern of this.INTENT_PATTERNS) {
|
|
185
|
+
const match = prompt.match(pattern.pattern);
|
|
186
|
+
if (match) {
|
|
187
|
+
const extracted = pattern.extractor(match, prompt);
|
|
188
|
+
const baseConfidence = this.calculateBaseConfidence(prompt, match[0]);
|
|
189
|
+
const contextBoost = this.calculateContextBoost(prompt);
|
|
190
|
+
const intent = {
|
|
191
|
+
category: pattern.category,
|
|
192
|
+
action: extracted.action || 'unknown',
|
|
193
|
+
entities: extracted.entities || [],
|
|
194
|
+
confidence: Math.min(baseConfidence + contextBoost, 1.0)
|
|
195
|
+
};
|
|
196
|
+
if (intent.confidence > highestConfidence) {
|
|
197
|
+
highestConfidence = intent.confidence;
|
|
198
|
+
bestMatch = intent;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return bestMatch;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Extract detailed components based on intent
|
|
206
|
+
*/
|
|
207
|
+
extractComponents(prompt, intent) {
|
|
208
|
+
switch (intent.category) {
|
|
209
|
+
case 'location':
|
|
210
|
+
return this.extractLocationComponents(prompt, intent);
|
|
211
|
+
case 'tool':
|
|
212
|
+
return this.extractToolComponents(prompt, intent);
|
|
213
|
+
case 'style':
|
|
214
|
+
return this.extractStyleComponents(prompt, intent);
|
|
215
|
+
case 'process':
|
|
216
|
+
return this.extractProcessComponents(prompt, intent);
|
|
217
|
+
default:
|
|
218
|
+
return { raw: intent };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Build the final preference object
|
|
223
|
+
*/
|
|
224
|
+
buildPreference(intent, components, rawText) {
|
|
225
|
+
const overrideSignals = this.detectOverrideSignals(rawText);
|
|
226
|
+
return {
|
|
227
|
+
key: components.key,
|
|
228
|
+
value: components.value,
|
|
229
|
+
confidence: intent.confidence,
|
|
230
|
+
rawText: rawText.trim(),
|
|
231
|
+
intent,
|
|
232
|
+
isOverride: overrideSignals.length > 0,
|
|
233
|
+
overrideSignals
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Extract location-specific components
|
|
238
|
+
*/
|
|
239
|
+
extractLocationComponents(prompt, intent) {
|
|
240
|
+
if (intent.entities.length < 2)
|
|
241
|
+
return null;
|
|
242
|
+
const [subject, location] = intent.entities;
|
|
243
|
+
const normalizedSubject = this.normalizeEntity(subject);
|
|
244
|
+
const normalizedLocation = this.normalizeLocation(location);
|
|
245
|
+
// Determine the preference key based on the subject
|
|
246
|
+
let key = 'file_location'; // default
|
|
247
|
+
if (normalizedSubject.match(/test|spec/i)) {
|
|
248
|
+
key = 'test_location';
|
|
249
|
+
}
|
|
250
|
+
else if (normalizedSubject.match(/config|settings/i)) {
|
|
251
|
+
key = 'config_location';
|
|
252
|
+
}
|
|
253
|
+
else if (normalizedSubject.match(/doc|documentation/i)) {
|
|
254
|
+
key = 'docs_location';
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
key,
|
|
258
|
+
value: normalizedLocation
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Extract tool-specific components
|
|
263
|
+
*/
|
|
264
|
+
extractToolComponents(prompt, intent) {
|
|
265
|
+
if (intent.entities.length === 0)
|
|
266
|
+
return null;
|
|
267
|
+
const tool = intent.entities[0];
|
|
268
|
+
const purpose = intent.entities[1] || 'general';
|
|
269
|
+
// Map tools to preference keys
|
|
270
|
+
const toolMappings = {
|
|
271
|
+
'axios': 'http_client',
|
|
272
|
+
'fetch': 'http_client',
|
|
273
|
+
'jest': 'test_framework',
|
|
274
|
+
'vitest': 'test_framework',
|
|
275
|
+
'mocha': 'test_framework',
|
|
276
|
+
'webpack': 'build_tool',
|
|
277
|
+
'vite': 'build_tool',
|
|
278
|
+
'typescript': 'language',
|
|
279
|
+
'javascript': 'language',
|
|
280
|
+
'tabs': 'indentation',
|
|
281
|
+
'2 spaces': 'indentation',
|
|
282
|
+
'4 spaces': 'indentation'
|
|
283
|
+
};
|
|
284
|
+
const key = toolMappings[tool.toLowerCase()] || 'tool_preference';
|
|
285
|
+
return {
|
|
286
|
+
key,
|
|
287
|
+
value: tool.toLowerCase()
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Extract style-specific components
|
|
292
|
+
*/
|
|
293
|
+
extractStyleComponents(prompt, intent) {
|
|
294
|
+
if (intent.entities.length === 0)
|
|
295
|
+
return null;
|
|
296
|
+
const style = intent.entities[0];
|
|
297
|
+
// Handle different style preferences
|
|
298
|
+
if (intent.action === 'style_preference' && intent.entities.length >= 2) {
|
|
299
|
+
const [value, type] = intent.entities;
|
|
300
|
+
if (type.includes('indent')) {
|
|
301
|
+
return {
|
|
302
|
+
key: 'indentation',
|
|
303
|
+
value: value
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
key: 'indentation',
|
|
309
|
+
value: style
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Extract process-specific components
|
|
314
|
+
*/
|
|
315
|
+
extractProcessComponents(prompt, intent) {
|
|
316
|
+
if (intent.entities.length === 0)
|
|
317
|
+
return null;
|
|
318
|
+
const process = intent.entities[0];
|
|
319
|
+
const action = intent.action;
|
|
320
|
+
return {
|
|
321
|
+
key: `${action}_rule`,
|
|
322
|
+
value: process
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Calculate base confidence based on match quality
|
|
327
|
+
*/
|
|
328
|
+
calculateBaseConfidence(fullText, matchedText) {
|
|
329
|
+
// Start with base confidence
|
|
330
|
+
let confidence = 0.6;
|
|
331
|
+
// Boost for match coverage
|
|
332
|
+
const coverage = matchedText.length / fullText.length;
|
|
333
|
+
confidence += coverage * 0.2;
|
|
334
|
+
// Boost for sentence position (preferences often at start or end)
|
|
335
|
+
const position = fullText.indexOf(matchedText) / fullText.length;
|
|
336
|
+
if (position < 0.2 || position > 0.8) {
|
|
337
|
+
confidence += 0.1;
|
|
338
|
+
}
|
|
339
|
+
return confidence;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Calculate confidence boost from context
|
|
343
|
+
*/
|
|
344
|
+
calculateContextBoost(text) {
|
|
345
|
+
let boost = 0;
|
|
346
|
+
const lowerText = text.toLowerCase();
|
|
347
|
+
// Check for context boosters
|
|
348
|
+
for (const [category, phrases] of Object.entries(this.CONTEXT_BOOSTERS)) {
|
|
349
|
+
for (const phrase of phrases) {
|
|
350
|
+
if (lowerText.includes(phrase)) {
|
|
351
|
+
switch (category) {
|
|
352
|
+
case 'temporal':
|
|
353
|
+
boost += 0.15;
|
|
354
|
+
break;
|
|
355
|
+
case 'change':
|
|
356
|
+
boost += 0.12;
|
|
357
|
+
break;
|
|
358
|
+
case 'emphasis':
|
|
359
|
+
boost += 0.08;
|
|
360
|
+
break;
|
|
361
|
+
case 'casual':
|
|
362
|
+
boost += 0.05;
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return Math.min(boost, 0.3); // Cap total boost
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Normalize entity names using synonyms
|
|
372
|
+
*/
|
|
373
|
+
normalizeEntity(entity) {
|
|
374
|
+
const lower = entity.toLowerCase().trim();
|
|
375
|
+
// Check synonyms
|
|
376
|
+
for (const [normalized, synonyms] of Object.entries(this.ENTITY_SYNONYMS)) {
|
|
377
|
+
if (synonyms.some(syn => lower.includes(syn))) {
|
|
378
|
+
return normalized;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return entity.trim();
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Normalize location paths
|
|
385
|
+
*/
|
|
386
|
+
normalizeLocation(location) {
|
|
387
|
+
// Remove quotes and extra spaces
|
|
388
|
+
let normalized = location.trim().replace(/["']/g, '');
|
|
389
|
+
// Remove temporal phrases and punctuation that might have been captured
|
|
390
|
+
normalized = normalized.replace(/\s+(from\s+now\s+on|going\s+forward|moving\s+forward)[\?\!]*$/i, '');
|
|
391
|
+
normalized = normalized.replace(/[\?\!]+$/, ''); // Remove trailing punctuation
|
|
392
|
+
// Don't add ./ prefix to already valid paths or simple directory names
|
|
393
|
+
if (!normalized.startsWith('/') &&
|
|
394
|
+
!normalized.startsWith('.') &&
|
|
395
|
+
!normalized.match(/^[a-zA-Z0-9_-]+$/) &&
|
|
396
|
+
!normalized.match(/^__[a-zA-Z0-9_-]+__$/)) { // Handle __dirname__ style
|
|
397
|
+
// Only add ./ if it contains spaces or special chars
|
|
398
|
+
normalized = `./${normalized}`;
|
|
399
|
+
}
|
|
400
|
+
// Normalize separators
|
|
401
|
+
normalized = normalized.replace(/\\/g, '/');
|
|
402
|
+
return normalized;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Detect override signals in text
|
|
406
|
+
*/
|
|
407
|
+
detectOverrideSignals(text) {
|
|
408
|
+
const signals = [];
|
|
409
|
+
const lowerText = text.toLowerCase();
|
|
410
|
+
for (const [category, phrases] of Object.entries(this.CONTEXT_BOOSTERS)) {
|
|
411
|
+
if (category === 'temporal' || category === 'change') {
|
|
412
|
+
for (const phrase of phrases) {
|
|
413
|
+
if (lowerText.includes(phrase)) {
|
|
414
|
+
signals.push(phrase);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return signals;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Split text into meaningful chunks for analysis
|
|
423
|
+
*/
|
|
424
|
+
splitIntoMeaningfulChunks(text) {
|
|
425
|
+
// Split by punctuation and conjunctions
|
|
426
|
+
const chunks = text.split(/[.!?;]|\s+(?:and|but|also|then)\s+/i);
|
|
427
|
+
return chunks
|
|
428
|
+
.map(chunk => chunk.trim())
|
|
429
|
+
.filter(chunk => chunk.length > 10); // Filter short fragments
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
exports.SemanticPreferenceExtractor = SemanticPreferenceExtractor;
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# Claude Recall MCP API Reference
|
|
2
|
+
|
|
3
|
+
## Protocol Version
|
|
4
|
+
- JSON-RPC 2.0
|
|
5
|
+
- MCP Protocol Version: 2024-11-05
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
### mcp__claude-recall__store_memory
|
|
10
|
+
|
|
11
|
+
Stores a memory in the Claude Recall database.
|
|
12
|
+
|
|
13
|
+
**Input Schema:**
|
|
14
|
+
```json
|
|
15
|
+
{
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"content": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "Memory content to store"
|
|
21
|
+
},
|
|
22
|
+
"metadata": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"description": "Optional metadata for the memory"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"required": ["content"]
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Output:**
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"id": "memory_1234567890_abc123",
|
|
35
|
+
"success": true,
|
|
36
|
+
"message": "Memory stored successfully"
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### mcp__claude-recall__search
|
|
41
|
+
|
|
42
|
+
Searches memories using natural language query.
|
|
43
|
+
|
|
44
|
+
**Input Schema:**
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"type": "object",
|
|
48
|
+
"properties": {
|
|
49
|
+
"query": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"description": "Search query"
|
|
52
|
+
},
|
|
53
|
+
"limit": {
|
|
54
|
+
"type": "number",
|
|
55
|
+
"description": "Maximum number of results",
|
|
56
|
+
"default": 10
|
|
57
|
+
},
|
|
58
|
+
"threshold": {
|
|
59
|
+
"type": "number",
|
|
60
|
+
"description": "Minimum similarity score (0-1)",
|
|
61
|
+
"default": 0.7
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"required": ["query"]
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Output:**
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"results": [
|
|
72
|
+
{
|
|
73
|
+
"id": "memory_1234567890_abc123",
|
|
74
|
+
"content": "Memory content",
|
|
75
|
+
"metadata": {},
|
|
76
|
+
"timestamp": "2024-01-01T00:00:00Z",
|
|
77
|
+
"score": 0.95
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"total": 1,
|
|
81
|
+
"query": "search query"
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### mcp__claude-recall__retrieve_memory
|
|
86
|
+
|
|
87
|
+
Retrieves specific memories by ID or recent memories.
|
|
88
|
+
|
|
89
|
+
**Input Schema:**
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"type": "object",
|
|
93
|
+
"properties": {
|
|
94
|
+
"id": {
|
|
95
|
+
"type": "string",
|
|
96
|
+
"description": "Memory ID to retrieve"
|
|
97
|
+
},
|
|
98
|
+
"limit": {
|
|
99
|
+
"type": "number",
|
|
100
|
+
"description": "Number of recent memories to retrieve",
|
|
101
|
+
"default": 10
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Output:**
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"memories": [
|
|
111
|
+
{
|
|
112
|
+
"id": "memory_1234567890_abc123",
|
|
113
|
+
"content": "Memory content",
|
|
114
|
+
"metadata": {},
|
|
115
|
+
"timestamp": "2024-01-01T00:00:00Z",
|
|
116
|
+
"sessionId": "session_123"
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
"total": 1
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### mcp__claude-recall__get_stats
|
|
124
|
+
|
|
125
|
+
Returns memory usage statistics.
|
|
126
|
+
|
|
127
|
+
**Input Schema:**
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"type": "object",
|
|
131
|
+
"properties": {}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Output:**
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"totalMemories": 100,
|
|
139
|
+
"sessionMemories": 25,
|
|
140
|
+
"memoryTypes": {
|
|
141
|
+
"general": 50,
|
|
142
|
+
"code": 30,
|
|
143
|
+
"conversation": 20
|
|
144
|
+
},
|
|
145
|
+
"lastMemoryTimestamp": "2024-01-01T00:00:00Z",
|
|
146
|
+
"sessionStartTime": "2024-01-01T00:00:00Z",
|
|
147
|
+
"databaseSize": "1.5 MB"
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### mcp__claude-recall__clear_context
|
|
152
|
+
|
|
153
|
+
Clears session-specific context memories.
|
|
154
|
+
|
|
155
|
+
**Input Schema:**
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"type": "object",
|
|
159
|
+
"properties": {
|
|
160
|
+
"confirm": {
|
|
161
|
+
"type": "boolean",
|
|
162
|
+
"description": "Confirmation required to clear context"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Output:**
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"success": true,
|
|
172
|
+
"message": "Context cleared successfully",
|
|
173
|
+
"memoriesCleared": 10
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Health Check
|
|
178
|
+
|
|
179
|
+
### Method: health/check
|
|
180
|
+
|
|
181
|
+
Returns server health information.
|
|
182
|
+
|
|
183
|
+
**Response:**
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"status": "healthy",
|
|
187
|
+
"version": "1.0.0",
|
|
188
|
+
"uptime": 3600,
|
|
189
|
+
"memory": {
|
|
190
|
+
"heapUsed": 50331648,
|
|
191
|
+
"heapTotal": 68157440,
|
|
192
|
+
"external": 2097152,
|
|
193
|
+
"rss": 104857600
|
|
194
|
+
},
|
|
195
|
+
"sessions": {
|
|
196
|
+
"total": 10,
|
|
197
|
+
"active": 2
|
|
198
|
+
},
|
|
199
|
+
"toolsRegistered": 5,
|
|
200
|
+
"database": "connected",
|
|
201
|
+
"rateLimiter": {
|
|
202
|
+
"requestsPerMinute": 100,
|
|
203
|
+
"currentRequests": 15
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Error Responses
|
|
209
|
+
|
|
210
|
+
All errors follow the JSON-RPC 2.0 error format:
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"jsonrpc": "2.0",
|
|
215
|
+
"id": 1,
|
|
216
|
+
"error": {
|
|
217
|
+
"code": -32603,
|
|
218
|
+
"message": "Internal error",
|
|
219
|
+
"data": {
|
|
220
|
+
"details": "Additional error information"
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Common Error Codes
|
|
227
|
+
- `-32700`: Parse error
|
|
228
|
+
- `-32600`: Invalid request
|
|
229
|
+
- `-32601`: Method not found
|
|
230
|
+
- `-32602`: Invalid params
|
|
231
|
+
- `-32603`: Internal error
|
|
232
|
+
- `-32000`: Tool not found
|
|
233
|
+
- `-32001`: Rate limit exceeded
|
|
234
|
+
- `-32002`: Database error
|
|
235
|
+
|
|
236
|
+
## Transport
|
|
237
|
+
|
|
238
|
+
The MCP server uses stdio transport:
|
|
239
|
+
- Input: stdin (JSON-RPC requests)
|
|
240
|
+
- Output: stdout (JSON-RPC responses)
|
|
241
|
+
- Errors: stderr (logging and diagnostics)
|
|
242
|
+
|
|
243
|
+
## Session Management
|
|
244
|
+
|
|
245
|
+
Sessions are automatically managed by the server:
|
|
246
|
+
- Session ID is generated on first connection
|
|
247
|
+
- Sessions persist across server restarts
|
|
248
|
+
- Session data stored in `~/.claude-recall/sessions.json`
|
|
249
|
+
- Memories are associated with sessions for context
|
|
250
|
+
|
|
251
|
+
## Rate Limiting
|
|
252
|
+
|
|
253
|
+
Default rate limiting configuration:
|
|
254
|
+
- 100 requests per minute per session
|
|
255
|
+
- Sliding window algorithm
|
|
256
|
+
- Rate limit resets after 60 seconds
|
|
257
|
+
- Custom limits via `CLAUDE_RECALL_RATE_LIMIT` environment variable
|