@claudetools/tools 0.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 +86 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +61 -0
- package/dist/handlers/tool-handlers.d.ts +2 -0
- package/dist/handlers/tool-handlers.js +1108 -0
- package/dist/helpers/api-client.d.ts +50 -0
- package/dist/helpers/api-client.js +60 -0
- package/dist/helpers/config-manager.d.ts +68 -0
- package/dist/helpers/config-manager.js +306 -0
- package/dist/helpers/config.d.ts +55 -0
- package/dist/helpers/config.js +174 -0
- package/dist/helpers/dependencies.d.ts +30 -0
- package/dist/helpers/dependencies.js +87 -0
- package/dist/helpers/formatter.d.ts +2 -0
- package/dist/helpers/formatter.js +24 -0
- package/dist/helpers/patterns.d.ts +15 -0
- package/dist/helpers/patterns.js +118 -0
- package/dist/helpers/project-registration.d.ts +27 -0
- package/dist/helpers/project-registration.js +338 -0
- package/dist/helpers/tasks.d.ts +152 -0
- package/dist/helpers/tasks.js +274 -0
- package/dist/helpers/workers.d.ts +18 -0
- package/dist/helpers/workers.js +146 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +75 -0
- package/dist/logger.d.ts +32 -0
- package/dist/logger.js +401 -0
- package/dist/prompts.d.ts +2 -0
- package/dist/prompts.js +64 -0
- package/dist/resources.d.ts +2 -0
- package/dist/resources.js +79 -0
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +206 -0
- package/dist/tools.d.ts +2 -0
- package/dist/tools.js +748 -0
- package/package.json +58 -0
package/dist/logger.js
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// ClaudeTools Memory MCP Server - Structured Logger
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// File-based logging for real-time monitoring of MCP operations
|
|
5
|
+
// =============================================================================
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
// Emojis for visual scanning
|
|
9
|
+
const CATEGORY_ICONS = {
|
|
10
|
+
TOOL: '🔧',
|
|
11
|
+
API: '🌐',
|
|
12
|
+
MEMORY: '🧠',
|
|
13
|
+
SEARCH: '🔍',
|
|
14
|
+
INJECT: '💉',
|
|
15
|
+
EXTRACT: '📤',
|
|
16
|
+
STORE: '💾',
|
|
17
|
+
QUERY: '❓',
|
|
18
|
+
IMPACT: '💥',
|
|
19
|
+
PATTERN: '🔬',
|
|
20
|
+
CONFIG: '⚙️',
|
|
21
|
+
REGISTRATION: '📝',
|
|
22
|
+
ERROR: '❌',
|
|
23
|
+
};
|
|
24
|
+
const LEVEL_ICONS = {
|
|
25
|
+
DEBUG: '🔹',
|
|
26
|
+
INFO: '🔷',
|
|
27
|
+
WARN: '⚠️',
|
|
28
|
+
ERROR: '❌',
|
|
29
|
+
};
|
|
30
|
+
class MCPLogger {
|
|
31
|
+
logFile;
|
|
32
|
+
logDir;
|
|
33
|
+
enabled;
|
|
34
|
+
constructor() {
|
|
35
|
+
// Log to .logs directory in project root
|
|
36
|
+
this.logDir = path.join(process.cwd(), '.logs');
|
|
37
|
+
this.logFile = path.join(this.logDir, 'mcp-server.log');
|
|
38
|
+
this.enabled = process.env.MCP_LOGGING !== 'false';
|
|
39
|
+
// Ensure log directory exists
|
|
40
|
+
if (this.enabled) {
|
|
41
|
+
try {
|
|
42
|
+
if (!fs.existsSync(this.logDir)) {
|
|
43
|
+
fs.mkdirSync(this.logDir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// If we can't create dir, disable logging
|
|
48
|
+
this.enabled = false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
formatTimestamp() {
|
|
53
|
+
const now = new Date();
|
|
54
|
+
return now.toISOString();
|
|
55
|
+
}
|
|
56
|
+
formatForFile(entry) {
|
|
57
|
+
const icon = CATEGORY_ICONS[entry.category] || '📝';
|
|
58
|
+
const levelIcon = LEVEL_ICONS[entry.level];
|
|
59
|
+
// Format timestamp as readable time only (HH:MM:SS)
|
|
60
|
+
const time = new Date(entry.timestamp).toLocaleTimeString('en-AU', {
|
|
61
|
+
hour12: false,
|
|
62
|
+
hour: '2-digit',
|
|
63
|
+
minute: '2-digit',
|
|
64
|
+
second: '2-digit',
|
|
65
|
+
});
|
|
66
|
+
// Build human-readable message
|
|
67
|
+
let line = `${time} ${levelIcon} ${icon} ${entry.message}`;
|
|
68
|
+
// Add duration as natural language
|
|
69
|
+
if (entry.duration !== undefined) {
|
|
70
|
+
if (entry.duration < 100) {
|
|
71
|
+
line += ` — instant`;
|
|
72
|
+
}
|
|
73
|
+
else if (entry.duration < 500) {
|
|
74
|
+
line += ` — ${entry.duration}ms`;
|
|
75
|
+
}
|
|
76
|
+
else if (entry.duration < 1000) {
|
|
77
|
+
line += ` — took ${entry.duration}ms`;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
line += ` — took ${(entry.duration / 1000).toFixed(1)}s`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Format data as natural language (no JSON)
|
|
84
|
+
if (entry.data && Object.keys(entry.data).length > 0) {
|
|
85
|
+
const details = this.formatDataAsNaturalLanguage(entry.data);
|
|
86
|
+
if (details) {
|
|
87
|
+
line += `\n ${details}`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return line;
|
|
91
|
+
}
|
|
92
|
+
formatDataAsNaturalLanguage(data) {
|
|
93
|
+
const parts = [];
|
|
94
|
+
for (const [key, value] of Object.entries(data)) {
|
|
95
|
+
// Skip tool name (already in message)
|
|
96
|
+
if (key === 'tool')
|
|
97
|
+
continue;
|
|
98
|
+
// Format different types naturally
|
|
99
|
+
if (key === 'args' && typeof value === 'object' && value !== null) {
|
|
100
|
+
const args = value;
|
|
101
|
+
const argParts = [];
|
|
102
|
+
for (const [argKey, argVal] of Object.entries(args)) {
|
|
103
|
+
if (typeof argVal === 'string') {
|
|
104
|
+
// Truncate long strings
|
|
105
|
+
const str = argVal.length > 80 ? argVal.slice(0, 77) + '...' : argVal;
|
|
106
|
+
argParts.push(`${argKey}: "${str}"`);
|
|
107
|
+
}
|
|
108
|
+
else if (typeof argVal === 'number') {
|
|
109
|
+
argParts.push(`${argKey}: ${argVal}`);
|
|
110
|
+
}
|
|
111
|
+
else if (typeof argVal === 'boolean') {
|
|
112
|
+
argParts.push(`${argKey}: ${argVal ? 'yes' : 'no'}`);
|
|
113
|
+
}
|
|
114
|
+
else if (Array.isArray(argVal)) {
|
|
115
|
+
argParts.push(`${argKey}: [${argVal.length} items]`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (argParts.length > 0) {
|
|
119
|
+
parts.push(`with ${argParts.join(', ')}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else if (key === 'method') {
|
|
123
|
+
parts.push(`using ${value}`);
|
|
124
|
+
}
|
|
125
|
+
else if (key === 'queryLength') {
|
|
126
|
+
parts.push(`(${value} chars)`);
|
|
127
|
+
}
|
|
128
|
+
else if (typeof value === 'string' && value.length < 100) {
|
|
129
|
+
parts.push(`${key}: ${value}`);
|
|
130
|
+
}
|
|
131
|
+
else if (typeof value === 'number') {
|
|
132
|
+
parts.push(`${key}: ${value}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return parts.join(' • ');
|
|
136
|
+
}
|
|
137
|
+
write(entry) {
|
|
138
|
+
if (!this.enabled)
|
|
139
|
+
return;
|
|
140
|
+
try {
|
|
141
|
+
const line = this.formatForFile(entry) + '\n';
|
|
142
|
+
fs.appendFileSync(this.logFile, line);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// Silently fail if we can't write
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// Core Logging Methods
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
debug(category, message, data) {
|
|
152
|
+
this.write({
|
|
153
|
+
timestamp: this.formatTimestamp(),
|
|
154
|
+
level: 'DEBUG',
|
|
155
|
+
category,
|
|
156
|
+
message,
|
|
157
|
+
data,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
info(category, message, data) {
|
|
161
|
+
this.write({
|
|
162
|
+
timestamp: this.formatTimestamp(),
|
|
163
|
+
level: 'INFO',
|
|
164
|
+
category,
|
|
165
|
+
message,
|
|
166
|
+
data,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
warn(category, message, data) {
|
|
170
|
+
this.write({
|
|
171
|
+
timestamp: this.formatTimestamp(),
|
|
172
|
+
level: 'WARN',
|
|
173
|
+
category,
|
|
174
|
+
message,
|
|
175
|
+
data,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
error(category, message, error) {
|
|
179
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
180
|
+
this.write({
|
|
181
|
+
timestamp: this.formatTimestamp(),
|
|
182
|
+
level: 'ERROR',
|
|
183
|
+
category,
|
|
184
|
+
message: `${message}: ${errorMessage}`,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
// Structured Operation Logging (Human-Readable)
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
toolCall(toolName, args) {
|
|
191
|
+
// Format tool name in a friendly way
|
|
192
|
+
const friendlyName = toolName.replace(/_/g, ' ');
|
|
193
|
+
const sanitized = this.sanitizeArgs(args);
|
|
194
|
+
// Build a natural language description
|
|
195
|
+
let description = `Starting ${friendlyName}`;
|
|
196
|
+
// Add context from args
|
|
197
|
+
if (sanitized.query && typeof sanitized.query === 'string') {
|
|
198
|
+
const query = sanitized.query;
|
|
199
|
+
const preview = query.length > 50 ? query.slice(0, 47) + '...' : query;
|
|
200
|
+
description = `Searching for "${preview}"`;
|
|
201
|
+
}
|
|
202
|
+
else if (sanitized.function_name) {
|
|
203
|
+
description = `Analysing function "${sanitized.function_name}"`;
|
|
204
|
+
}
|
|
205
|
+
else if (sanitized.entity1 && sanitized.relationship && sanitized.entity2) {
|
|
206
|
+
description = `Storing: ${sanitized.entity1} → ${sanitized.relationship} → ${sanitized.entity2}`;
|
|
207
|
+
}
|
|
208
|
+
else if (sanitized.code && typeof sanitized.code === 'string') {
|
|
209
|
+
description = `Checking ${(sanitized.code).length} characters of code`;
|
|
210
|
+
}
|
|
211
|
+
this.info('TOOL', description);
|
|
212
|
+
}
|
|
213
|
+
toolResult(toolName, success, duration, resultSummary) {
|
|
214
|
+
const friendlyName = toolName.replace(/_/g, ' ');
|
|
215
|
+
if (success) {
|
|
216
|
+
const message = resultSummary ? resultSummary : `${friendlyName} completed`;
|
|
217
|
+
this.write({
|
|
218
|
+
timestamp: this.formatTimestamp(),
|
|
219
|
+
level: 'INFO',
|
|
220
|
+
category: 'TOOL',
|
|
221
|
+
message,
|
|
222
|
+
duration,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
this.write({
|
|
227
|
+
timestamp: this.formatTimestamp(),
|
|
228
|
+
level: 'ERROR',
|
|
229
|
+
category: 'TOOL',
|
|
230
|
+
message: `${friendlyName} failed`,
|
|
231
|
+
duration,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
apiRequest(method, endpoint) {
|
|
236
|
+
// Make endpoint human-readable
|
|
237
|
+
const path = endpoint.replace('/api/v1/', '').replace(/\//g, ' → ');
|
|
238
|
+
this.debug('API', `${method} request to ${path}`);
|
|
239
|
+
}
|
|
240
|
+
apiResponse(method, endpoint, status, duration) {
|
|
241
|
+
const level = status >= 400 ? 'ERROR' : 'DEBUG';
|
|
242
|
+
const path = endpoint.replace('/api/v1/', '').replace(/\//g, ' → ');
|
|
243
|
+
const outcome = status >= 400 ? 'failed' : 'succeeded';
|
|
244
|
+
this.write({
|
|
245
|
+
timestamp: this.formatTimestamp(),
|
|
246
|
+
level,
|
|
247
|
+
category: 'API',
|
|
248
|
+
message: `${method} ${path} ${outcome} (${status})`,
|
|
249
|
+
duration,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
searchQuery(query, method) {
|
|
253
|
+
const preview = query.length > 50 ? query.slice(0, 47) + '...' : query;
|
|
254
|
+
this.info('SEARCH', `Looking for "${preview}" using ${method}`);
|
|
255
|
+
}
|
|
256
|
+
searchResults(factsFound, entitiesFound, duration) {
|
|
257
|
+
let message;
|
|
258
|
+
if (factsFound === 0 && entitiesFound === 0) {
|
|
259
|
+
message = 'No results found';
|
|
260
|
+
}
|
|
261
|
+
else if (factsFound === 0) {
|
|
262
|
+
message = `Found ${entitiesFound} ${entitiesFound === 1 ? 'entity' : 'entities'}, no facts`;
|
|
263
|
+
}
|
|
264
|
+
else if (entitiesFound === 0) {
|
|
265
|
+
message = `Found ${factsFound} ${factsFound === 1 ? 'fact' : 'facts'}, no entities`;
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
message = `Found ${factsFound} ${factsFound === 1 ? 'fact' : 'facts'} and ${entitiesFound} ${entitiesFound === 1 ? 'entity' : 'entities'}`;
|
|
269
|
+
}
|
|
270
|
+
this.write({
|
|
271
|
+
timestamp: this.formatTimestamp(),
|
|
272
|
+
level: 'INFO',
|
|
273
|
+
category: 'SEARCH',
|
|
274
|
+
message,
|
|
275
|
+
duration,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
memoryStore(entity1, relation, entity2) {
|
|
279
|
+
// Make the relationship human-readable
|
|
280
|
+
const readableRelation = relation.toLowerCase().replace(/_/g, ' ');
|
|
281
|
+
this.info('STORE', `Remembered: "${entity1}" ${readableRelation} "${entity2}"`);
|
|
282
|
+
}
|
|
283
|
+
queryDependencies(functionName, direction, resultsCount, duration) {
|
|
284
|
+
let message;
|
|
285
|
+
if (direction === 'forward') {
|
|
286
|
+
message = resultsCount === 0
|
|
287
|
+
? `"${functionName}" doesn't call any other functions`
|
|
288
|
+
: `"${functionName}" calls ${resultsCount} other ${resultsCount === 1 ? 'function' : 'functions'}`;
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
message = resultsCount === 0
|
|
292
|
+
? `Nothing calls "${functionName}"`
|
|
293
|
+
: `${resultsCount} ${resultsCount === 1 ? 'function calls' : 'functions call'} "${functionName}"`;
|
|
294
|
+
}
|
|
295
|
+
this.write({
|
|
296
|
+
timestamp: this.formatTimestamp(),
|
|
297
|
+
level: 'INFO',
|
|
298
|
+
category: 'QUERY',
|
|
299
|
+
message,
|
|
300
|
+
duration,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
impactAnalysis(functionName, analysisType, riskLevel, totalAffected, duration) {
|
|
304
|
+
const riskEmoji = riskLevel === 'CRITICAL' ? '🚨' : riskLevel === 'HIGH' ? '⚠️' : riskLevel === 'MEDIUM' ? '📋' : '✅';
|
|
305
|
+
let message;
|
|
306
|
+
if (totalAffected === 0) {
|
|
307
|
+
message = `${riskEmoji} Changing "${functionName}" is safe — no dependencies`;
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
message = `${riskEmoji} Changing "${functionName}" affects ${totalAffected} ${totalAffected === 1 ? 'place' : 'places'} — ${riskLevel.toLowerCase()} risk`;
|
|
311
|
+
}
|
|
312
|
+
this.write({
|
|
313
|
+
timestamp: this.formatTimestamp(),
|
|
314
|
+
level: 'INFO',
|
|
315
|
+
category: 'IMPACT',
|
|
316
|
+
message,
|
|
317
|
+
duration,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
patternCheck(codeLength, warningsFound, securityScore, perfScore, duration) {
|
|
321
|
+
let message;
|
|
322
|
+
if (warningsFound === 0) {
|
|
323
|
+
message = `Code looks good — no issues found`;
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
message = `Found ${warningsFound} ${warningsFound === 1 ? 'issue' : 'issues'}`;
|
|
327
|
+
}
|
|
328
|
+
// Add scores as context
|
|
329
|
+
const secLabel = securityScore >= 80 ? 'secure' : securityScore >= 50 ? 'some concerns' : 'needs attention';
|
|
330
|
+
const perfLabel = perfScore >= 80 ? 'efficient' : perfScore >= 50 ? 'acceptable' : 'could be faster';
|
|
331
|
+
message += ` — Security: ${secLabel} (${securityScore}/100), Performance: ${perfLabel} (${perfScore}/100)`;
|
|
332
|
+
this.write({
|
|
333
|
+
timestamp: this.formatTimestamp(),
|
|
334
|
+
level: 'INFO',
|
|
335
|
+
category: 'PATTERN',
|
|
336
|
+
message,
|
|
337
|
+
duration,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
contextInjection(query, factsIncluded, tokenBudget, duration) {
|
|
341
|
+
const preview = query.length > 35 ? query.slice(0, 32) + '...' : query;
|
|
342
|
+
let message;
|
|
343
|
+
if (factsIncluded === 0) {
|
|
344
|
+
message = `No relevant context found for "${preview}"`;
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
message = `Providing ${factsIncluded} ${factsIncluded === 1 ? 'piece' : 'pieces'} of context for "${preview}"`;
|
|
348
|
+
}
|
|
349
|
+
this.write({
|
|
350
|
+
timestamp: this.formatTimestamp(),
|
|
351
|
+
level: 'INFO',
|
|
352
|
+
category: 'INJECT',
|
|
353
|
+
message,
|
|
354
|
+
duration,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
// ---------------------------------------------------------------------------
|
|
358
|
+
// Utility
|
|
359
|
+
// ---------------------------------------------------------------------------
|
|
360
|
+
sanitizeArgs(args) {
|
|
361
|
+
const sanitized = {};
|
|
362
|
+
for (const [key, value] of Object.entries(args)) {
|
|
363
|
+
if (typeof value === 'string' && value.length > 200) {
|
|
364
|
+
sanitized[key] = value.slice(0, 200) + `... (${value.length} chars)`;
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
sanitized[key] = value;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return sanitized;
|
|
371
|
+
}
|
|
372
|
+
// Start a timing context
|
|
373
|
+
startTimer() {
|
|
374
|
+
const start = Date.now();
|
|
375
|
+
return () => Date.now() - start;
|
|
376
|
+
}
|
|
377
|
+
// Log separator for visual clarity
|
|
378
|
+
separator(label) {
|
|
379
|
+
if (label) {
|
|
380
|
+
this.write({
|
|
381
|
+
timestamp: this.formatTimestamp(),
|
|
382
|
+
level: 'INFO',
|
|
383
|
+
category: 'TOOL',
|
|
384
|
+
message: `${'─'.repeat(20)} ${label} ${'─'.repeat(20)}`,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
// Clear log file
|
|
389
|
+
clear() {
|
|
390
|
+
if (this.enabled) {
|
|
391
|
+
try {
|
|
392
|
+
fs.writeFileSync(this.logFile, '');
|
|
393
|
+
}
|
|
394
|
+
catch {
|
|
395
|
+
// Silently fail
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
// Singleton export
|
|
401
|
+
export const mcpLogger = new MCPLogger();
|
package/dist/prompts.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// MCP Prompt Handlers
|
|
3
|
+
// =============================================================================
|
|
4
|
+
import { ListPromptsRequestSchema, GetPromptRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
export function registerPromptHandlers(server) {
|
|
6
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
7
|
+
prompts: [
|
|
8
|
+
{
|
|
9
|
+
name: 'recall',
|
|
10
|
+
description: 'Recall information from memory about a specific topic',
|
|
11
|
+
arguments: [
|
|
12
|
+
{
|
|
13
|
+
name: 'topic',
|
|
14
|
+
description: 'The topic to recall information about',
|
|
15
|
+
required: true,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'remember',
|
|
21
|
+
description: 'Store important information to memory',
|
|
22
|
+
arguments: [
|
|
23
|
+
{
|
|
24
|
+
name: 'information',
|
|
25
|
+
description: 'The information to remember',
|
|
26
|
+
required: true,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
}));
|
|
32
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
33
|
+
const { name, arguments: args } = request.params;
|
|
34
|
+
if (name === 'recall') {
|
|
35
|
+
const topic = args?.topic || 'general';
|
|
36
|
+
return {
|
|
37
|
+
messages: [
|
|
38
|
+
{
|
|
39
|
+
role: 'user',
|
|
40
|
+
content: {
|
|
41
|
+
type: 'text',
|
|
42
|
+
text: `Search your memory for information about: ${topic}\n\nUse the memory_search tool to find relevant facts and entities.`,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (name === 'remember') {
|
|
49
|
+
const information = args?.information || '';
|
|
50
|
+
return {
|
|
51
|
+
messages: [
|
|
52
|
+
{
|
|
53
|
+
role: 'user',
|
|
54
|
+
content: {
|
|
55
|
+
type: 'text',
|
|
56
|
+
text: `Store this information to memory:\n\n${information}\n\nUse the memory_store_fact tool to save any specific facts, or memory_add for general conversation context.`,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// MCP Resource Handlers
|
|
3
|
+
// =============================================================================
|
|
4
|
+
import { ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { getSummary, getEntities, getContext } from './helpers/api-client.js';
|
|
6
|
+
import { formatContextForClaude } from './helpers/formatter.js';
|
|
7
|
+
import { getDefaultProjectId } from './helpers/config.js';
|
|
8
|
+
export function registerResourceHandlers(server) {
|
|
9
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
10
|
+
resources: [
|
|
11
|
+
{
|
|
12
|
+
uri: `memory://summary`,
|
|
13
|
+
name: 'Memory Summary',
|
|
14
|
+
description: 'Current state of the memory system',
|
|
15
|
+
mimeType: 'text/plain',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
uri: `memory://entities`,
|
|
19
|
+
name: 'Entity List',
|
|
20
|
+
description: 'All known entities in the memory system',
|
|
21
|
+
mimeType: 'application/json',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
uri: `memory://context`,
|
|
25
|
+
name: 'Current Context',
|
|
26
|
+
description: 'Recent facts and entities from memory',
|
|
27
|
+
mimeType: 'text/markdown',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
}));
|
|
31
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
32
|
+
const uri = request.params.uri;
|
|
33
|
+
try {
|
|
34
|
+
// Get project ID (may throw if not configured)
|
|
35
|
+
const projectId = getDefaultProjectId();
|
|
36
|
+
if (uri === 'memory://summary') {
|
|
37
|
+
const summary = await getSummary(projectId);
|
|
38
|
+
return {
|
|
39
|
+
contents: [
|
|
40
|
+
{
|
|
41
|
+
uri,
|
|
42
|
+
mimeType: 'text/plain',
|
|
43
|
+
text: summary,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (uri === 'memory://entities') {
|
|
49
|
+
const entities = await getEntities(projectId);
|
|
50
|
+
return {
|
|
51
|
+
contents: [
|
|
52
|
+
{
|
|
53
|
+
uri,
|
|
54
|
+
mimeType: 'application/json',
|
|
55
|
+
text: JSON.stringify(entities, null, 2),
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (uri === 'memory://context') {
|
|
61
|
+
const context = await getContext(projectId);
|
|
62
|
+
return {
|
|
63
|
+
contents: [
|
|
64
|
+
{
|
|
65
|
+
uri,
|
|
66
|
+
mimeType: 'text/markdown',
|
|
67
|
+
text: formatContextForClaude(context),
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
76
|
+
throw new Error(`Failed to read resource: ${message}`);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
package/dist/setup.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runSetup(): Promise<void>;
|