mongodb-memory-bank-mcp
Version:
FIXED: MongoDB Memory Bank MCP with bulletproof error handling, smart operations, and session state management. Eliminates [object Object] errors and user confusion.
353 lines (352 loc) âĸ 14.9 kB
JavaScript
/**
* MongoDB Index Manager - Programmatic Index Creation
*
* Following EXACT MongoDB official patterns for programmatic index creation:
* 1. Vector Search Index (Atlas) - using createSearchIndex with type: "vectorSearch"
* 2. Text Search Index - using createIndex for text search
* 3. Compound Indexes - for efficient filtering and sorting
*
* Based on official MongoDB documentation:
* - https://www.mongodb.com/docs/drivers/node/current/indexes/
* - https://www.mongodb.com/docs/atlas/atlas-search/manage-indexes/
* - MongoDB Node.js Driver v6.17+ patterns
* - Community forum examples: https://www.mongodb.com/community/forums/t/can-i-create-a-vectorsearch-index-with-createsearchindex-command/265546
*/
import { MongoDBConnection } from '../../../infra/mongodb/connection/mongodb-connection.js';
import { getCollectionNames } from '../../../main/config/mongodb-config.js';
/**
* Creates MongoDB indexes following official patterns
* Supports both Atlas (with vector search) and Community Edition
*/
export async function createMongoDBIndexes() {
const result = {
success: false,
message: '',
indexes: {},
recommendations: []
};
try {
console.log('đ§ Creating MongoDB indexes using official patterns...');
const mongoConnection = MongoDBConnection.getInstance();
const db = await mongoConnection.getDatabase();
const collectionNames = getCollectionNames();
const memoriesCollection = collectionNames.memories;
const projectsCollection = collectionNames.projects;
const memoriesCol = db.collection(memoriesCollection);
const projectsCol = db.collection(projectsCollection);
// 1. VECTOR SEARCH INDEX (Atlas only)
// Following official Node.js driver pattern from MongoDB docs
try {
console.log('đ Creating Vector Search Index (Atlas)...');
const vectorSearchIndex = {
name: "vector_search_index",
type: "vectorSearch",
definition: {
fields: [{
path: "contentVector",
type: "vector",
numDimensions: 1024, // Voyage AI embedding dimensions
similarity: "cosine"
}]
}
};
// Use official createSearchIndex method
await memoriesCol.createSearchIndex(vectorSearchIndex);
result.indexes.vectorSearch = {
created: true,
name: "vector_search_index",
status: "created",
message: "â
Vector search index created successfully using createSearchIndex()"
};
console.log('â
Vector search index created');
}
catch (vectorError) {
console.log('â ī¸ Vector search index creation failed (expected for non-Atlas):', vectorError.message);
result.indexes.vectorSearch = {
created: false,
name: "vector_search_index",
status: "failed",
message: `Vector search not available: ${vectorError.message}`
};
if (vectorError.message.includes('command not found') ||
vectorError.message.includes('not supported')) {
result.recommendations.push('Vector search requires MongoDB Atlas M10+ cluster');
result.recommendations.push('For local development, text search will be used instead');
}
}
// 2. TEXT SEARCH INDEX
// Following official createIndex pattern for text search
try {
console.log('đ Creating Text Search Index...');
const textIndexResult = await memoriesCol.createIndex({
content: "text",
fileName: "text",
summary: "text",
tags: "text"
}, {
name: "text_search_index",
default_language: "english",
weights: {
content: 10,
summary: 5,
fileName: 3,
tags: 2
}
});
result.indexes.textSearch = {
created: true,
name: "text_search_index",
status: "created",
message: `â
Text search index created: ${textIndexResult}`
};
console.log('â
Text search index created');
}
catch (textError) {
console.log('â Text search index creation failed:', textError.message);
result.indexes.textSearch = {
created: false,
name: "text_search_index",
status: "failed",
message: `Text search index failed: ${textError.message}`
};
}
// 3. COMPOUND INDEXES
// Following official createIndex patterns for compound indexes
try {
console.log('đ Creating Compound Indexes...');
const compoundIndexes = [
{
name: "project_filename_index",
fields: { projectName: 1, fileName: 1 },
options: { unique: true }
},
{
name: "project_modified_index",
fields: { projectName: 1, lastModified: -1 }
},
{
name: "project_tags_index",
fields: { projectName: 1, tags: 1 }
},
{
name: "memory_type_index",
fields: { projectName: 1, memoryType: 1, lastModified: -1 }
}
];
const compoundResults = [];
for (const index of compoundIndexes) {
try {
const indexResult = await memoriesCol.createIndex(index.fields, { name: index.name, ...index.options });
compoundResults.push({
name: index.name,
fields: index.fields,
status: `created: ${indexResult}`
});
console.log(`â
Compound index created: ${index.name}`);
}
catch (indexError) {
compoundResults.push({
name: index.name,
fields: index.fields,
status: `failed: ${indexError.message}`
});
console.log(`â ī¸ Compound index failed: ${index.name} - ${indexError.message}`);
}
}
result.indexes.compound = {
created: compoundResults.some(r => r.status.includes('created')),
indexes: compoundResults
};
}
catch (compoundError) {
console.log('â Compound indexes creation failed:', compoundError.message);
}
// 4. PROJECT COLLECTION INDEXES
try {
console.log('đ Creating Project Collection Indexes...');
await projectsCol.createIndex({ projectName: 1 }, { unique: true });
await projectsCol.createIndex({ lastAccessed: -1 });
await projectsCol.createIndex({ status: 1, lastAccessed: -1 });
console.log('â
Project collection indexes created');
}
catch (projectError) {
console.log('â ī¸ Project indexes creation failed:', projectError.message);
}
// Determine overall success
const hasWorkingSearch = result.indexes.vectorSearch?.created || result.indexes.textSearch?.created;
const hasCompoundIndexes = result.indexes.compound?.created || false;
result.success = (hasWorkingSearch || false) && hasCompoundIndexes;
if (result.success) {
result.message = 'đ MongoDB indexes created successfully using official patterns';
result.recommendations.push('Indexes are ready for hybrid search operations');
result.recommendations.push('Use memory_search tool to test search functionality');
}
else {
result.message = 'â ī¸ Some indexes failed to create - check individual status';
result.recommendations.push('Review error messages and MongoDB configuration');
result.recommendations.push('Ensure proper MongoDB version and permissions');
}
return result;
}
catch (error) {
console.error('â Index creation failed:', error);
result.success = false;
result.message = `Index creation failed: ${error.message}`;
result.recommendations.push('Check MongoDB connection and permissions');
result.recommendations.push('Verify MongoDB version supports required index types');
return result;
}
}
/**
* Alternative Vector Search Index Creation using MongoDB Atlas Admin API
* For cases where driver method fails or for more control
* Based on: https://www.mongodb.com/docs/atlas/reference/api/fts-indexes/
*/
export async function createVectorSearchIndexViaAPI(clusterName, databaseName, collectionName, apiKey, projectId) {
try {
console.log('đ Creating Vector Search Index via Atlas Admin API...');
if (!apiKey || !projectId) {
return {
success: false,
message: 'Atlas Admin API requires API key and project ID',
recommendation: 'Use driver method instead or provide Atlas credentials'
};
}
// Atlas Admin API endpoint for creating search indexes
const apiUrl = `https://cloud.mongodb.com/api/atlas/v1.0/groups/${projectId}/clusters/${clusterName}/fts/indexes`;
const indexDefinition = {
name: "vector_search_index_api",
type: "vectorSearch",
database: databaseName,
collectionName: collectionName,
definition: {
fields: [{
path: "contentVector",
type: "vector",
numDimensions: 1024,
similarity: "cosine"
}]
}
};
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify(indexDefinition)
});
if (response.ok) {
const result = await response.json();
return {
success: true,
message: 'Vector search index created via Atlas Admin API',
indexId: result.indexId,
status: result.status
};
}
else {
const error = await response.text();
return {
success: false,
message: `Atlas API error: ${response.status} - ${error}`,
recommendation: 'Check API credentials and cluster configuration'
};
}
}
catch (error) {
console.error('â Atlas API index creation failed:', error);
return {
success: false,
message: `Atlas API failed: ${error.message}`,
recommendation: 'Use driver method instead'
};
}
}
/**
* Alternative using MongoDB command for older versions
* Based on community forum examples
*/
export async function createVectorSearchIndexViaCommand() {
try {
console.log('âī¸ Creating Vector Search Index via MongoDB command...');
const mongoConnection = MongoDBConnection.getInstance();
const db = await mongoConnection.getDatabase();
const collectionNames = getCollectionNames();
const memoriesCollection = collectionNames.memories;
// Using the command approach from community forums
const command = {
createSearchIndexes: memoriesCollection,
indexes: [{
name: "vector_search_index_cmd",
type: "vectorSearch",
definition: {
fields: [{
path: "contentVector",
type: "vector",
numDimensions: 1024,
similarity: "cosine"
}]
}
}]
};
const result = await db.command(command);
return {
success: true,
message: 'Vector search index created via MongoDB command',
result: result
};
}
catch (error) {
console.error('â Command-based index creation failed:', error);
return {
success: false,
message: `Command failed: ${error.message}`,
recommendation: 'This method requires MongoDB Atlas M10+ cluster'
};
}
}
/**
* Lists all indexes on memory bank collections
* Following official listIndexes() pattern
*/
export async function listMongoDBIndexes() {
try {
const mongoConnection = MongoDBConnection.getInstance();
const db = await mongoConnection.getDatabase();
const collectionNames = getCollectionNames();
const memoriesCollection = collectionNames.memories;
const projectsCollection = collectionNames.projects;
const memoriesCol = db.collection(memoriesCollection);
const projectsCol = db.collection(projectsCollection);
console.log('đ Listing MongoDB indexes...');
const memoriesIndexes = await memoriesCol.listIndexes().toArray();
const projectsIndexes = await projectsCol.listIndexes().toArray();
// Also try to list search indexes if available
let searchIndexes = [];
try {
searchIndexes = await memoriesCol.listSearchIndexes().toArray();
console.log(`đ Found ${searchIndexes.length} search indexes`);
}
catch (searchError) {
console.log('âšī¸ Search indexes not available (expected for non-Atlas)');
}
return {
success: true,
indexes: {
memories: memoriesIndexes,
projects: projectsIndexes,
search: searchIndexes
},
message: `Found ${memoriesIndexes.length} memory indexes, ${projectsIndexes.length} project indexes, ${searchIndexes.length} search indexes`
};
}
catch (error) {
console.error('â Failed to list indexes:', error);
return {
success: false,
message: `Failed to list indexes: ${error.message}`,
indexes: {}
};
}
}