strategic-intelligence-mcp
Version:
Strategic Intelligence MCP Server - connecting technical progress to business outcomes with systematic strategic planning
217 lines • 9.52 kB
JavaScript
export class JSONStorageAdapter {
dataPath;
cache = null;
constructor(dataPath = './strategic-data.json') {
// For ES modules, we'll use process.cwd() + relative path resolution
if (!dataPath.startsWith('/')) {
this.dataPath = `${process.cwd()}/${dataPath}`;
}
else {
this.dataPath = dataPath;
}
console.error(`Strategic Intelligence MCP: Storage adapter initialized with path: ${this.dataPath}`);
}
async save(data) {
try {
const fs = await import('fs/promises');
const path = await import('path');
// Ensure directory exists
const dir = path.dirname(this.dataPath);
await fs.mkdir(dir, { recursive: true });
// Write data atomically with backup
const tempPath = `${this.dataPath}.tmp`;
await fs.writeFile(tempPath, JSON.stringify(data, null, 2));
await fs.rename(tempPath, this.dataPath);
this.cache = data;
console.error(`Strategic Intelligence MCP: Data saved successfully to ${this.dataPath}`);
}
catch (error) {
console.error(`Strategic Intelligence MCP: Failed to save data to ${this.dataPath}:`, error);
throw new Error(`Failed to save strategic data: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async load() {
if (this.cache) {
console.error(`Strategic Intelligence MCP: Using cached data from ${this.dataPath}`);
return this.cache;
}
try {
console.error(`Strategic Intelligence MCP: Loading data from ${this.dataPath}`);
const fs = await import('fs/promises');
const path = await import('path');
// Check if file exists
try {
await fs.access(this.dataPath);
console.error(`Strategic Intelligence MCP: Data file exists at ${this.dataPath}`);
}
catch (accessError) {
console.error(`Strategic Intelligence MCP: Data file does not exist at ${this.dataPath}, will create new one`);
throw accessError; // Re-throw to trigger empty database creation
}
const fileContent = await fs.readFile(this.dataPath, 'utf-8');
console.error(`Strategic Intelligence MCP: File content loaded, size: ${fileContent.length} bytes`);
// Validate JSON
let parsedData;
try {
parsedData = JSON.parse(fileContent);
console.error(`Strategic Intelligence MCP: JSON parsed successfully`);
}
catch (parseError) {
console.error(`Strategic Intelligence MCP: JSON parse error:`, parseError);
throw new Error(`Invalid JSON in strategic data file: ${parseError instanceof Error ? parseError.message : 'Unknown parse error'}`);
}
// Validate data structure
if (!parsedData || typeof parsedData !== 'object') {
throw new Error('Invalid strategic data structure: not an object');
}
// Ensure required properties exist
if (!parsedData.conversations)
parsedData.conversations = {};
if (!parsedData.goals)
parsedData.goals = {};
if (!parsedData.alignments)
parsedData.alignments = {};
if (!parsedData.insights)
parsedData.insights = {};
if (!parsedData.metadata) {
parsedData.metadata = {
version: '1.0.0',
lastUpdated: new Date().toISOString(),
totalConversations: Object.keys(parsedData.conversations).length,
totalGoals: Object.keys(parsedData.goals).length,
totalInsights: Object.keys(parsedData.insights).length
};
}
this.cache = parsedData;
console.error(`Strategic Intelligence MCP: Data loaded successfully with ${Object.keys(parsedData.conversations).length} conversations, ${Object.keys(parsedData.goals).length} goals`);
return this.cache;
}
catch (error) {
console.error(`Strategic Intelligence MCP: Failed to load data from ${this.dataPath}:`, error);
console.error(`Strategic Intelligence MCP: Creating new empty database`);
// Return empty database if file doesn't exist or is corrupted
const emptyDatabase = {
conversations: {},
goals: {},
alignments: {},
insights: {},
metadata: {
version: '1.0.0',
lastUpdated: new Date().toISOString(),
totalConversations: 0,
totalGoals: 0,
totalInsights: 0
}
};
try {
await this.save(emptyDatabase);
console.error(`Strategic Intelligence MCP: Empty database created and saved successfully`);
}
catch (saveError) {
console.error(`Strategic Intelligence MCP: Failed to save empty database:`, saveError);
// Continue with in-memory database if we can't save
this.cache = emptyDatabase;
}
return emptyDatabase;
}
}
async findConversationsByType(type) {
const data = await this.load();
return Object.values(data.conversations).filter(conv => conv.type === type);
}
async findConversationsByDateRange(from, to) {
const data = await this.load();
const fromDate = new Date(from);
const toDate = new Date(to);
return Object.values(data.conversations).filter(conv => {
const convDate = new Date(conv.timestamp);
return convDate >= fromDate && convDate <= toDate;
});
}
async getGoalsByStatus(status) {
const data = await this.load();
return Object.values(data.goals).filter(goal => goal.status === status);
}
async getGoalsByCategory(category) {
const data = await this.load();
return Object.values(data.goals).filter(goal => goal.category === category);
}
async searchInsights(query, category) {
const data = await this.load();
const searchTerm = query.toLowerCase();
return Object.values(data.insights).filter(insight => {
const matchesSearch = insight.content.toLowerCase().includes(searchTerm);
const matchesCategory = !category || insight.category === category;
return matchesSearch && matchesCategory;
});
}
async getInsightsByImpact(impact) {
const data = await this.load();
return Object.values(data.insights).filter(insight => insight.impact === impact);
}
async getAlignmentsByTechnicalFeature(feature) {
const data = await this.load();
const searchTerm = feature.toLowerCase();
return Object.values(data.alignments).filter(alignment => alignment.technicalFeature.toLowerCase().includes(searchTerm));
}
async getAlignmentsByBusinessGoal(goalId) {
const data = await this.load();
return Object.values(data.alignments).filter(alignment => alignment.businessValue.primaryGoals.includes(goalId));
}
async getConversationInsightCounts() {
const data = await this.load();
return Object.values(data.conversations).map(conv => ({
conversationId: conv.id,
insightCount: conv.insights.length
}));
}
async getGoalProgressSummary() {
const data = await this.load();
return Object.values(data.goals).map(goal => {
const latestProgress = goal.progressHistory[goal.progressHistory.length - 1];
return {
goalId: goal.id,
completion: latestProgress?.completion || 0,
confidence: goal.confidence
};
});
}
async getRecentActivity(days) {
const data = await this.load();
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - days);
const activity = [];
// Add conversations
Object.values(data.conversations).forEach(conv => {
if (new Date(conv.timestamp) >= cutoffDate) {
activity.push({
type: 'conversation',
id: conv.id,
timestamp: conv.timestamp
});
}
});
// Add goals
Object.values(data.goals).forEach(goal => {
if (new Date(goal.lastUpdated) >= cutoffDate) {
activity.push({
type: 'goal',
id: goal.id,
timestamp: goal.lastUpdated
});
}
});
// Add insights
Object.values(data.insights).forEach(insight => {
if (new Date(insight.timestamp) >= cutoffDate) {
activity.push({
type: 'insight',
id: insight.id,
timestamp: insight.timestamp
});
}
});
return activity.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
}
}
//# sourceMappingURL=StorageAdapter.js.map