@qianjue/mcp-memory-server
Version:
A Model Context Protocol (MCP) server for intelligent memory management with vector search capabilities
868 lines • 33.1 kB
JavaScript
import { MemoryType, } from '../types/memory.js';
export class MemoryTools {
memoryManager;
constructor(memoryManager) {
this.memoryManager = memoryManager;
}
/**
* 获取所有工具定义
*/
getToolDefinitions() {
return [
{
name: 'create_memory',
description: 'Create a new memory entry with specified content and type',
inputSchema: {
type: 'object',
properties: {
content: {
type: 'string',
description: 'The content of the memory to store',
},
type: {
type: 'string',
enum: ['global', 'conversation', 'temporary'],
description: 'The type of memory (global, conversation, or temporary)',
default: 'conversation',
},
conversationId: {
type: 'string',
description: 'The conversation ID for conversation-type memories (optional)',
},
tags: {
type: 'array',
items: { type: 'string' },
description: 'Optional tags for categorizing the memory',
},
metadata: {
type: 'object',
description: 'Optional metadata object for additional information',
},
},
required: ['content'],
},
},
{
name: 'read_memories',
description: 'Read memories with optional filtering',
inputSchema: {
type: 'object',
properties: {
type: {
type: 'string',
enum: ['global', 'conversation', 'temporary'],
description: 'Filter by memory type',
},
conversationId: {
type: 'string',
description: 'Filter by conversation ID',
},
tags: {
type: 'array',
items: { type: 'string' },
description: 'Filter by tags (memories containing any of these tags)',
},
searchText: {
type: 'string',
description: 'Search text to filter memories by content or tags',
},
limit: {
type: 'number',
description: 'Maximum number of memories to return',
minimum: 1,
},
offset: {
type: 'number',
description: 'Number of memories to skip (for pagination)',
minimum: 0,
},
},
},
},
{
name: 'update_memory',
description: 'Update an existing memory entry',
inputSchema: {
type: 'object',
properties: {
memoryId: {
type: 'string',
description: 'The UUID of the memory to update',
},
content: {
type: 'string',
description: 'New content for the memory',
},
tags: {
type: 'array',
items: { type: 'string' },
description: 'New tags for the memory',
},
metadata: {
type: 'object',
description: 'New metadata for the memory (will be merged with existing)',
},
},
required: ['memoryId'],
},
},
{
name: 'delete_memory',
description: 'Delete a memory entry by ID',
inputSchema: {
type: 'object',
properties: {
memoryId: {
type: 'string',
description: 'The UUID of the memory to delete',
},
},
required: ['memoryId'],
},
},
{
name: 'set_storage_path',
description: 'Set the storage path for memory files',
inputSchema: {
type: 'object',
properties: {
path: {
type: 'string',
description: 'The file system path where memory files should be stored',
},
},
required: ['path'],
},
},
{
name: 'get_memory_stats',
description: 'Get statistics about stored memories',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'cleanup_old_conversations',
description: 'Clean up old conversation memory files',
inputSchema: {
type: 'object',
properties: {
maxAge: {
type: 'number',
description: 'Maximum age in milliseconds (default: 30 days)',
default: 2592000000,
},
},
},
},
{
name: 'list_memories',
description: 'List memories with smart filtering - combines current conversation and global memories for quick access',
inputSchema: {
type: 'object',
properties: {
conversationId: {
type: 'string',
description: 'Current conversation ID to include conversation-specific memories',
},
query: {
type: 'string',
description: 'Search query to filter memories by content, tags, or metadata',
},
includeGlobal: {
type: 'boolean',
description: 'Whether to include global memories (default: true)',
default: true,
},
includeConversation: {
type: 'boolean',
description: 'Whether to include conversation memories (default: true)',
default: true,
},
limit: {
type: 'number',
description: 'Maximum number of memories to return (default: 20)',
default: 20,
minimum: 1,
maximum: 100,
},
sortBy: {
type: 'string',
enum: ['relevance', 'date', 'type'],
description: 'Sort memories by relevance, date, or type (default: relevance)',
default: 'relevance',
},
},
},
},
{
name: 'query_memories',
description: 'Advanced query interface for finding specific memories with detailed filtering',
inputSchema: {
type: 'object',
properties: {
searchText: {
type: 'string',
description: 'Text to search for in memory content and tags',
},
tags: {
type: 'array',
items: { type: 'string' },
description: 'Filter by specific tags (memories containing any of these tags)',
},
type: {
type: 'string',
enum: ['global', 'conversation', 'temporary'],
description: 'Filter by memory type',
},
conversationId: {
type: 'string',
description: 'Filter by specific conversation ID',
},
dateRange: {
type: 'object',
properties: {
from: {
type: 'string',
description: 'Start date (ISO 8601 format)',
},
to: {
type: 'string',
description: 'End date (ISO 8601 format)',
},
},
description: 'Filter by date range',
},
metadata: {
type: 'object',
description: 'Filter by metadata key-value pairs',
},
limit: {
type: 'number',
description: 'Maximum number of results (default: 50)',
default: 50,
minimum: 1,
maximum: 200,
},
offset: {
type: 'number',
description: 'Number of results to skip for pagination (default: 0)',
default: 0,
minimum: 0,
},
},
},
},
{
name: 'get_enhanced_stats',
description: 'Get comprehensive statistics including memory, cache, index, and performance metrics',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'configure_embedding',
description: 'Configure embedding provider for vector search functionality',
inputSchema: {
type: 'object',
properties: {
provider: {
type: 'string',
enum: ['ollama', 'gemini', 'openai'],
description: 'The embedding provider to use',
},
apiKey: {
type: 'string',
description: 'API key for the provider (required for gemini and openai)',
},
baseUrl: {
type: 'string',
description: 'Base URL for the provider API (required for ollama, optional for others)',
},
model: {
type: 'string',
description: 'Model name to use for embeddings',
},
dimensions: {
type: 'number',
description: 'Embedding dimensions (optional, will be auto-detected if not provided)',
},
timeout: {
type: 'number',
description: 'Request timeout in milliseconds (default: 30000)',
default: 30000,
},
maxRetries: {
type: 'number',
description: 'Maximum number of retries for failed requests (default: 3)',
default: 3,
},
},
required: ['provider', 'model'],
},
},
{
name: 'semantic_search',
description: 'Search memories using semantic similarity based on meaning rather than exact keywords',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'The search query text',
},
limit: {
type: 'number',
description: 'Maximum number of results to return (default: 10)',
default: 10,
minimum: 1,
maximum: 50,
},
threshold: {
type: 'number',
description: 'Minimum similarity threshold (0-1, default: 0.7)',
default: 0.7,
minimum: 0,
maximum: 1,
},
includeContent: {
type: 'boolean',
description: 'Whether to include full content in results (default: true)',
default: true,
},
includeMetadata: {
type: 'boolean',
description: 'Whether to include metadata in results (default: true)',
default: true,
},
hybridSearch: {
type: 'boolean',
description: 'Whether to combine semantic and keyword search (default: false)',
default: false,
},
keywordWeight: {
type: 'number',
description: 'Weight for keyword search in hybrid mode (0-1, default: 0.3)',
default: 0.3,
minimum: 0,
maximum: 1,
},
},
required: ['query'],
},
},
{
name: 'generate_embeddings',
description: "Generate embedding vectors for existing memories that don't have them",
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'calculate_similarity',
description: 'Calculate semantic similarity between two text strings',
inputSchema: {
type: 'object',
properties: {
text1: {
type: 'string',
description: 'First text to compare',
},
text2: {
type: 'string',
description: 'Second text to compare',
},
},
required: ['text1', 'text2'],
},
},
{
name: 'get_vector_stats',
description: 'Get statistics about the vector store and embedding configuration',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'create_folder',
description: 'Create a new folder for organizing memories',
inputSchema: {
type: 'object',
properties: {
folderPath: {
type: 'string',
description: 'The path of the folder to create (e.g., "Work/ProjectA" or "Personal/Study")',
},
description: {
type: 'string',
description: 'Optional description of the folder',
},
},
required: ['folderPath'],
},
},
{
name: 'delete_folder',
description: 'Delete a folder and optionally its memories',
inputSchema: {
type: 'object',
properties: {
folderPath: {
type: 'string',
description: 'The path of the folder to delete',
},
deleteMemories: {
type: 'boolean',
description: 'Whether to delete all memories in the folder (default: false)',
default: false,
},
},
required: ['folderPath'],
},
},
{
name: 'rename_folder',
description: 'Rename a folder and update all memories within it',
inputSchema: {
type: 'object',
properties: {
oldPath: {
type: 'string',
description: 'The current path of the folder',
},
newPath: {
type: 'string',
description: 'The new path for the folder',
},
},
required: ['oldPath', 'newPath'],
},
},
{
name: 'list_folders',
description: 'List all folders with their information',
inputSchema: {
type: 'object',
properties: {},
},
},
];
}
/**
* 调用指定的工具
*/
async callTool(name, args) {
switch (name) {
case 'create_memory':
return this.createMemory(args);
case 'read_memories':
return this.readMemories(args);
case 'update_memory':
return this.updateMemory(args);
case 'delete_memory':
return this.deleteMemory(args);
case 'set_storage_path':
return this.setStoragePath(args);
case 'get_memory_stats':
return this.getMemoryStats();
case 'cleanup_old_conversations':
return this.cleanupOldConversations(args);
case 'list_memories':
return this.listMemories(args);
case 'query_memories':
return this.queryMemories(args);
case 'get_enhanced_stats':
return this.getEnhancedStats();
case 'configure_embedding':
return this.configureEmbedding(args);
case 'semantic_search':
return this.semanticSearch(args);
case 'generate_embeddings':
return this.generateEmbeddings();
case 'calculate_similarity':
return this.calculateSimilarity(args);
case 'get_vector_stats':
return this.getVectorStats();
case 'create_folder':
return this.createFolder(args);
case 'delete_folder':
return this.deleteFolder(args);
case 'rename_folder':
return this.renameFolder(args);
case 'list_folders':
return this.listFolders();
default:
throw new Error(`Unknown tool: ${name}`);
}
}
/**
* 创建记忆工具实现
*/
async createMemory(args) {
const input = {
content: args.content,
type: args.type ? args.type : MemoryType.CONVERSATION,
conversationId: args.conversationId,
tags: args.tags,
metadata: args.metadata,
};
return await this.memoryManager.createMemory(input);
}
/**
* 读取记忆工具实现
*/
async readMemories(args) {
const filter = {
type: args.type ? args.type : undefined,
conversationId: args.conversationId,
tags: args.tags,
searchText: args.searchText,
limit: args.limit,
offset: args.offset,
};
return await this.memoryManager.readMemories(filter);
}
/**
* 更新记忆工具实现
*/
async updateMemory(args) {
const { memoryId, ...updateData } = args;
const input = {
content: updateData.content,
tags: updateData.tags,
metadata: updateData.metadata,
};
return await this.memoryManager.updateMemory(memoryId, input);
}
/**
* 删除记忆工具实现
*/
async deleteMemory(args) {
return await this.memoryManager.deleteMemory(args.memoryId);
}
/**
* 设置存储路径工具实现
*/
async setStoragePath(args) {
return await this.memoryManager.setStoragePath(args.path);
}
/**
* 获取记忆统计工具实现
*/
async getMemoryStats() {
return await this.memoryManager.getMemoryStats();
}
/**
* 清理旧对话工具实现
*/
async cleanupOldConversations(args) {
return await this.memoryManager.cleanupOldConversations(args.maxAge);
}
/**
* 列出记忆工具实现 - 智能过滤当前对话和全局记忆
*/
async listMemories(args) {
const { conversationId, query, includeGlobal = true, includeConversation = true, limit = 20, sortBy = 'relevance', } = args;
try {
let allMemories = [];
// 获取全局记忆
if (includeGlobal) {
const globalResult = await this.memoryManager.readMemories({
type: MemoryType.GLOBAL,
searchText: query,
limit: Math.ceil(limit / 2),
});
if (globalResult.success && globalResult.data) {
allMemories.push(...globalResult.data);
}
}
// 获取对话记忆
if (includeConversation && conversationId) {
const convResult = await this.memoryManager.readMemories({
type: MemoryType.CONVERSATION,
conversationId,
searchText: query,
limit: Math.ceil(limit / 2),
});
if (convResult.success && convResult.data) {
allMemories.push(...convResult.data);
}
}
// 排序
if (sortBy === 'date') {
allMemories.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
}
else if (sortBy === 'type') {
allMemories.sort((a, b) => a.type.localeCompare(b.type));
}
// 'relevance' 排序已经由搜索功能处理
// 限制结果数量
allMemories = allMemories.slice(0, limit);
return {
success: true,
data: allMemories,
message: `Found ${allMemories.length} memories`,
metadata: {
includeGlobal,
includeConversation,
conversationId,
query,
sortBy,
totalResults: allMemories.length,
},
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to list memories',
};
}
}
/**
* 高级查询记忆工具实现
*/
async queryMemories(args) {
const { searchText, tags, type, conversationId, dateRange, metadata, limit = 50, offset = 0, } = args;
try {
// 构建过滤器
const filter = {
searchText,
tags,
type: type ? type : undefined,
conversationId,
limit,
offset,
};
// 获取记忆
const result = await this.memoryManager.readMemories(filter);
if (!result.success || !result.data) {
return result;
}
let filteredMemories = result.data;
// 应用日期范围过滤
if (dateRange) {
const fromDate = dateRange.from ? new Date(dateRange.from) : null;
const toDate = dateRange.to ? new Date(dateRange.to) : null;
filteredMemories = filteredMemories.filter((memory) => {
const memoryDate = new Date(memory.createdAt);
if (fromDate && memoryDate < fromDate)
return false;
if (toDate && memoryDate > toDate)
return false;
return true;
});
}
// 应用元数据过滤
if (metadata && typeof metadata === 'object') {
filteredMemories = filteredMemories.filter((memory) => {
if (!memory.metadata)
return false;
for (const [key, value] of Object.entries(metadata)) {
if (memory.metadata[key] !== value)
return false;
}
return true;
});
}
return {
success: true,
data: filteredMemories,
message: `Found ${filteredMemories.length} memories matching query`,
metadata: {
searchText,
tags,
type,
conversationId,
dateRange,
appliedMetadataFilter: !!metadata,
totalResults: filteredMemories.length,
offset,
limit,
},
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to query memories',
};
}
}
/**
* 获取增强统计信息工具实现
*/
async getEnhancedStats() {
return await this.memoryManager.getEnhancedStats();
}
// ==================== 向量相关工具实现 ====================
/**
* 配置嵌入提供商工具实现
*/
async configureEmbedding(args) {
try {
const config = {
provider: args.provider,
apiKey: args.apiKey,
baseUrl: args.baseUrl,
model: args.model,
dimensions: args.dimensions,
timeout: args.timeout || 30000,
maxRetries: args.maxRetries || 3,
};
return await this.memoryManager.configureEmbedding(config);
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to configure embedding provider',
};
}
}
/**
* 语义搜索工具实现
*/
async semanticSearch(args) {
try {
const options = {
query: args.query,
limit: args.limit || 10,
threshold: args.threshold || 0.7,
includeContent: args.includeContent !== false,
includeMetadata: args.includeMetadata !== false,
hybridSearch: args.hybridSearch || false,
keywordWeight: args.keywordWeight || 0.3,
};
return await this.memoryManager.semanticSearch(options);
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to perform semantic search',
};
}
}
/**
* 生成嵌入向量工具实现
*/
async generateEmbeddings() {
try {
return await this.memoryManager.generateEmbeddingsForExistingMemories();
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to generate embeddings',
};
}
}
/**
* 计算相似度工具实现
*/
async calculateSimilarity(args) {
try {
const { text1, text2 } = args;
if (!text1 || !text2) {
return {
success: false,
error: 'Both text1 and text2 are required',
};
}
return await this.memoryManager.calculateSimilarity(text1, text2);
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to calculate similarity',
};
}
}
/**
* 获取向量统计信息工具实现
*/
async getVectorStats() {
try {
return await this.memoryManager.getVectorStats();
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to get vector statistics',
};
}
}
// ==================== 文件夹管理工具实现 ====================
/**
* 创建文件夹工具实现
*/
async createFolder(args) {
try {
const input = {
folderPath: args.folderPath,
description: args.description,
};
return await this.memoryManager.createFolder(input);
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to create folder',
};
}
}
/**
* 删除文件夹工具实现
*/
async deleteFolder(args) {
try {
const input = {
folderPath: args.folderPath,
deleteMemories: args.deleteMemories ?? false,
};
return await this.memoryManager.deleteFolder(input);
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to delete folder',
};
}
}
/**
* 重命名文件夹工具实现
*/
async renameFolder(args) {
try {
const input = {
oldPath: args.oldPath,
newPath: args.newPath,
};
return await this.memoryManager.renameFolder(input);
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to rename folder',
};
}
}
/**
* 列出文件夹工具实现
*/
async listFolders() {
try {
return await this.memoryManager.listFolders();
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to list folders',
};
}
}
}
//# sourceMappingURL=MemoryTools.js.map