@shirokuma-library/mcp-knowledge-base
Version:
MCP server for AI-powered knowledge management with semantic search, graph analysis, and automatic enrichment
78 lines (77 loc) • 2.93 kB
JavaScript
import { ClaudeInterface } from './ai/claude-interface.js';
import { EmbeddingManager } from './ai/embedding-manager.js';
import { DataStorage } from './ai/data-storage.js';
import { Item } from '../entities/Item.js';
export class EnhancedAIService {
dataSource;
claudeInterface;
embeddingManager;
dataStorage;
constructor(dataSource) {
this.dataSource = dataSource;
this.claudeInterface = new ClaudeInterface();
this.embeddingManager = new EmbeddingManager();
this.dataStorage = new DataStorage(dataSource);
}
async generateEnrichments(params) {
const weightedTexts = [];
if (params.title) {
weightedTexts.push(params.title);
}
if (params.description) {
weightedTexts.push(params.description);
}
if (params.content) {
weightedTexts.push(params.content);
}
const combinedContent = {
title: params.title || '',
description: params.description || '',
content: weightedTexts.join(' ')
};
return this.claudeInterface.extractWeightedKeywords(combinedContent);
}
async enrichItem(item) {
const enrichments = await this.generateEnrichments({
title: item.title,
description: item.description,
content: item.content
});
const itemRepo = this.dataSource.getRepository(Item);
await itemRepo.update(item.id, {
aiSummary: enrichments.summary,
searchIndex: enrichments.keywords.map(k => k.keyword).join(' '),
embedding: enrichments.embedding
});
if (enrichments.keywords.length > 0) {
await this.dataStorage.storeKeywordsForItem(item.id, enrichments.keywords);
}
if (enrichments.concepts.length > 0) {
await this.dataStorage.storeConceptsForItem(item.id, enrichments.concepts);
}
}
async findSimilarItems(itemId, limit = 10) {
const itemRepo = this.dataSource.getRepository(Item);
const item = await itemRepo.findOne({ where: { id: itemId } });
if (!item || !item.embedding) {
return [];
}
const items = await itemRepo
.createQueryBuilder('item')
.select(['item.id', 'item.embedding'])
.where('item.embedding IS NOT NULL')
.andWhere('item.id != :itemId', { itemId })
.getMany();
const similarities = items
.map(other => {
if (!other.embedding)
return null;
const similarity = this.embeddingManager.calculateSimilarity(item.embedding, other.embedding);
return { id: other.id, similarity };
})
.filter((s) => s !== null)
.sort((a, b) => b.similarity - a.similarity)
.slice(0, limit);
return similarities;
}
}