Skip to main content
Glama

memory_visualization

Generate visual representations of memory data using dashboards, timelines, networks, heatmaps, distributions, trends, or custom visualizations to analyze and understand memory usage patterns.

Instructions

Generate visual representations of memory data

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
visualizationTypeNodashboard
optionsNo
customVisualizationNo

Implementation Reference

  • Registration of the 'memory_visualization' tool in the memoryTools array, including full input schema for different visualization types and options.
    {
      name: "memory_visualization",
      description: "Generate visual representations of memory data",
      inputSchema: {
        type: "object",
        properties: {
          visualizationType: {
            type: "string",
            enum: [
              "dashboard",
              "timeline",
              "network",
              "heatmap",
              "distribution",
              "trends",
              "custom",
            ],
            default: "dashboard",
          },
          options: {
            type: "object",
            properties: {
              timeRange: {
                type: "object",
                properties: {
                  start: { type: "string", format: "date-time" },
                  end: { type: "string", format: "date-time" },
                },
              },
              includeCharts: { type: "array", items: { type: "string" } },
              config: {
                type: "object",
                properties: {
                  width: { type: "number", default: 800 },
                  height: { type: "number", default: 600 },
                  theme: {
                    type: "string",
                    enum: ["light", "dark", "auto"],
                    default: "light",
                  },
                  exportFormat: {
                    type: "string",
                    enum: ["svg", "png", "json", "html"],
                    default: "svg",
                  },
                  interactive: { type: "boolean", default: true },
                },
              },
            },
          },
          customVisualization: {
            type: "object",
            properties: {
              type: {
                type: "string",
                enum: [
                  "line",
                  "bar",
                  "scatter",
                  "heatmap",
                  "network",
                  "sankey",
                  "treemap",
                  "timeline",
                ],
              },
              query: {
                type: "object",
                properties: {
                  filters: { type: "object" },
                  groupBy: { type: "string" },
                  aggregation: { type: "string" },
                },
              },
            },
          },
        },
      },
    },
  • Main handler method generateDashboard that creates comprehensive memory dashboards with multiple chart types based on input parameters.
    async generateDashboard(options?: {
      timeRange?: { start: Date; end: Date };
      includeCharts?: string[];
      config?: Partial<VisualizationConfig>;
    }): Promise<DashboardData> {
      const timeRange = options?.timeRange || this.getDefaultTimeRange();
      const config = { ...this.defaultConfig, ...options?.config };
    
      this.emit("dashboard_generation_started", { timeRange });
    
      try {
        const charts: ChartData[] = [];
    
        // Activity Timeline
        if (
          !options?.includeCharts ||
          options.includeCharts.includes("activity")
        ) {
          charts.push(await this.generateActivityTimeline(timeRange, config));
        }
    
        // Memory Type Distribution
        if (
          !options?.includeCharts ||
          options.includeCharts.includes("distribution")
        ) {
          charts.push(
            await this.generateMemoryTypeDistribution(timeRange, config),
          );
        }
    
        // Success Rate Trends
        if (
          !options?.includeCharts ||
          options.includeCharts.includes("success")
        ) {
          charts.push(await this.generateSuccessRateTrends(timeRange, config));
        }
    
        // Knowledge Graph Network
        if (
          !options?.includeCharts ||
          options.includeCharts.includes("network")
        ) {
          charts.push(await this.generateKnowledgeGraphVisualization(config));
        }
    
        // Learning Patterns Heatmap
        if (
          !options?.includeCharts ||
          options.includeCharts.includes("learning")
        ) {
          charts.push(await this.generateLearningPatternsHeatmap(config));
        }
    
        // Temporal Patterns
        if (
          !options?.includeCharts ||
          options.includeCharts.includes("temporal")
        ) {
          charts.push(
            await this.generateTemporalPatternsChart(timeRange, config),
          );
        }
    
        // Project Correlation Matrix
        if (
          !options?.includeCharts ||
          options.includeCharts.includes("correlation")
        ) {
          charts.push(
            await this.generateProjectCorrelationMatrix(timeRange, config),
          );
        }
    
        // Get summary data
        const entries = await this.getEntriesInTimeRange(timeRange);
        const keyInsights = await this.generateKeyInsights(entries, timeRange);
        const healthScore = await this.calculateSystemHealthScore(entries);
    
        const dashboard: DashboardData = {
          title: "DocuMCP Memory System Dashboard",
          description: `Comprehensive overview of memory system activity from ${timeRange.start.toLocaleDateString()} to ${timeRange.end.toLocaleDateString()}`,
          charts,
          summary: {
            totalEntries: entries.length,
            timeRange,
            keyInsights,
            healthScore,
          },
          generated: new Date(),
        };
    
        this.emit("dashboard_generated", {
          charts: charts.length,
          entries: entries.length,
          timeRange,
        });
    
        return dashboard;
      } catch (error) {
        this.emit("dashboard_error", {
          error: error instanceof Error ? error.message : String(error),
        });
        throw error;
      }
    }
  • Core MemoryVisualizationSystem class containing all the logic for generating various memory visualizations including timelines, networks, heatmaps, and dashboards.
    export class MemoryVisualizationSystem extends EventEmitter {
      private storage: JSONLStorage;
      private manager: MemoryManager;
      private learningSystem: IncrementalLearningSystem;
      private knowledgeGraph: KnowledgeGraph;
      private temporalAnalysis: TemporalMemoryAnalysis;
      private defaultConfig: VisualizationConfig;
      private visualizationCache: Map<string, ChartData>;
    
      constructor(
        storage: JSONLStorage,
        manager: MemoryManager,
        learningSystem: IncrementalLearningSystem,
        knowledgeGraph: KnowledgeGraph,
        temporalAnalysis: TemporalMemoryAnalysis,
      ) {
        super();
        this.storage = storage;
        this.manager = manager;
        this.learningSystem = learningSystem;
        this.knowledgeGraph = knowledgeGraph;
        this.temporalAnalysis = temporalAnalysis;
        this.visualizationCache = new Map();
    
        this.defaultConfig = {
          width: 800,
          height: 600,
          theme: "light",
          colorScheme: [
            "#3B82F6", // Blue
            "#10B981", // Green
            "#F59E0B", // Yellow
            "#EF4444", // Red
            "#8B5CF6", // Purple
            "#06B6D4", // Cyan
            "#F97316", // Orange
            "#84CC16", // Lime
          ],
          interactive: true,
          exportFormat: "svg",
          responsive: true,
        };
      }
    
      /**
       * Generate comprehensive dashboard
       */
      async generateDashboard(options?: {
        timeRange?: { start: Date; end: Date };
        includeCharts?: string[];
        config?: Partial<VisualizationConfig>;
      }): Promise<DashboardData> {
        const timeRange = options?.timeRange || this.getDefaultTimeRange();
        const config = { ...this.defaultConfig, ...options?.config };
    
        this.emit("dashboard_generation_started", { timeRange });
    
        try {
          const charts: ChartData[] = [];
    
          // Activity Timeline
          if (
            !options?.includeCharts ||
            options.includeCharts.includes("activity")
          ) {
            charts.push(await this.generateActivityTimeline(timeRange, config));
          }
    
          // Memory Type Distribution
          if (
            !options?.includeCharts ||
            options.includeCharts.includes("distribution")
          ) {
            charts.push(
              await this.generateMemoryTypeDistribution(timeRange, config),
            );
          }
    
          // Success Rate Trends
          if (
            !options?.includeCharts ||
            options.includeCharts.includes("success")
          ) {
            charts.push(await this.generateSuccessRateTrends(timeRange, config));
          }
    
          // Knowledge Graph Network
          if (
            !options?.includeCharts ||
            options.includeCharts.includes("network")
          ) {
            charts.push(await this.generateKnowledgeGraphVisualization(config));
          }
    
          // Learning Patterns Heatmap
          if (
            !options?.includeCharts ||
            options.includeCharts.includes("learning")
          ) {
            charts.push(await this.generateLearningPatternsHeatmap(config));
          }
    
          // Temporal Patterns
          if (
            !options?.includeCharts ||
            options.includeCharts.includes("temporal")
          ) {
            charts.push(
              await this.generateTemporalPatternsChart(timeRange, config),
            );
          }
    
          // Project Correlation Matrix
          if (
            !options?.includeCharts ||
            options.includeCharts.includes("correlation")
          ) {
            charts.push(
              await this.generateProjectCorrelationMatrix(timeRange, config),
            );
          }
    
          // Get summary data
          const entries = await this.getEntriesInTimeRange(timeRange);
          const keyInsights = await this.generateKeyInsights(entries, timeRange);
          const healthScore = await this.calculateSystemHealthScore(entries);
    
          const dashboard: DashboardData = {
            title: "DocuMCP Memory System Dashboard",
            description: `Comprehensive overview of memory system activity from ${timeRange.start.toLocaleDateString()} to ${timeRange.end.toLocaleDateString()}`,
            charts,
            summary: {
              totalEntries: entries.length,
              timeRange,
              keyInsights,
              healthScore,
            },
            generated: new Date(),
          };
    
          this.emit("dashboard_generated", {
            charts: charts.length,
            entries: entries.length,
            timeRange,
          });
    
          return dashboard;
        } catch (error) {
          this.emit("dashboard_error", {
            error: error instanceof Error ? error.message : String(error),
          });
          throw error;
        }
      }
    
      /**
       * Generate activity timeline chart
       */
      async generateActivityTimeline(
        timeRange: { start: Date; end: Date },
        config: Partial<VisualizationConfig>,
      ): Promise<ChartData> {
        const entries = await this.getEntriesInTimeRange(timeRange);
    
        // Group entries by day
        const dailyData = new Map<string, number>();
        const successData = new Map<string, number>();
    
        for (const entry of entries) {
          const day = entry.timestamp.slice(0, 10); // YYYY-MM-DD
          dailyData.set(day, (dailyData.get(day) || 0) + 1);
    
          if (entry.data.outcome === "success" || entry.data.success === true) {
            successData.set(day, (successData.get(day) || 0) + 1);
          }
        }
    
        // Create time series data
        const datasets = [
          {
            label: "Total Activity",
            data: Array.from(dailyData.entries()).map(([date, count]) => ({
              x: date,
              y: count,
            })),
            borderColor: config.colorScheme?.[0] || "#3B82F6",
            backgroundColor: config.colorScheme?.[0] || "#3B82F6",
            fill: false,
          },
          {
            label: "Successful Activities",
            data: Array.from(successData.entries()).map(([date, count]) => ({
              x: date,
              y: count,
            })),
            borderColor: config.colorScheme?.[1] || "#10B981",
            backgroundColor: config.colorScheme?.[1] || "#10B981",
            fill: false,
          },
        ];
    
        return {
          type: "line",
          title: "Memory Activity Timeline",
          description:
            "Daily memory system activity showing total entries and successful outcomes",
          data: {
            datasets,
            options: {
              responsive: config.responsive,
              plugins: {
                title: {
                  display: true,
                  text: "Memory Activity Over Time",
                },
                legend: {
                  display: true,
                  position: "top",
                },
              },
              scales: {
                x: {
                  type: "time",
                  time: {
                    unit: "day",
                  },
                  title: {
                    display: true,
                    text: "Date",
                  },
                },
                y: {
                  title: {
                    display: true,
                    text: "Number of Entries",
                  },
                },
              },
            },
          },
          config,
          metadata: {
            generated: new Date(),
            dataPoints: entries.length,
            timeRange,
            filters: { type: "activity_timeline" },
          },
        };
      }
    
      /**
       * Generate memory type distribution chart
       */
      async generateMemoryTypeDistribution(
        timeRange: { start: Date; end: Date },
        config: Partial<VisualizationConfig>,
      ): Promise<ChartData> {
        const entries = await this.getEntriesInTimeRange(timeRange);
    
        // Count entries by type
        const typeCounts = new Map<string, number>();
        for (const entry of entries) {
          typeCounts.set(entry.type, (typeCounts.get(entry.type) || 0) + 1);
        }
    
        // Sort by count
        const sortedTypes = Array.from(typeCounts.entries()).sort(
          ([, a], [, b]) => b - a,
        );
    
        const data = {
          labels: sortedTypes.map(([type]) => type),
          datasets: [
            {
              data: sortedTypes.map(([, count]) => count),
              backgroundColor: config.colorScheme || this.defaultConfig.colorScheme,
              borderColor:
                config.colorScheme?.map((c) => this.darkenColor(c)) ||
                this.defaultConfig.colorScheme.map((c) => this.darkenColor(c)),
              borderWidth: 2,
            },
          ],
        };
    
        return {
          type: "bar",
          title: "Memory Type Distribution",
          description: "Distribution of memory entries by type",
          data: {
            ...data,
            options: {
              responsive: config.responsive,
              plugins: {
                title: {
                  display: true,
                  text: "Memory Entry Types",
                },
                legend: {
                  display: false,
                },
              },
              scales: {
                y: {
                  beginAtZero: true,
                  title: {
                    display: true,
                    text: "Number of Entries",
                  },
                },
                x: {
                  title: {
                    display: true,
                    text: "Memory Type",
                  },
                },
              },
            },
          },
          config,
          metadata: {
            generated: new Date(),
            dataPoints: entries.length,
            timeRange,
            filters: { type: "type_distribution" },
          },
        };
      }
    
      /**
       * Generate success rate trends chart
       */
      async generateSuccessRateTrends(
        timeRange: { start: Date; end: Date },
        config: Partial<VisualizationConfig>,
      ): Promise<ChartData> {
        const entries = await this.getEntriesInTimeRange(timeRange);
    
        // Group by week and calculate success rates
        const weeklyData = new Map<string, { total: number; successful: number }>();
    
        for (const entry of entries) {
          const week = this.getWeekKey(new Date(entry.timestamp));
          const current = weeklyData.get(week) || { total: 0, successful: 0 };
    
          current.total++;
          if (entry.data.outcome === "success" || entry.data.success === true) {
            current.successful++;
          }
    
          weeklyData.set(week, current);
        }
    
        // Calculate success rates
        const data = Array.from(weeklyData.entries())
          .map(([week, stats]) => ({
            x: week,
            y: stats.total > 0 ? (stats.successful / stats.total) * 100 : 0,
            total: stats.total,
            successful: stats.successful,
          }))
          .sort((a, b) => a.x.localeCompare(b.x));
    
        return {
          type: "line",
          title: "Success Rate Trends",
          description: "Weekly success rate trends for memory system operations",
          data: {
            datasets: [
              {
                label: "Success Rate (%)",
                data: data,
                borderColor: config.colorScheme?.[1] || "#10B981",
                backgroundColor: config.colorScheme?.[1] || "#10B981",
                fill: false,
                tension: 0.1,
              },
            ],
            options: {
              responsive: config.responsive,
              plugins: {
                title: {
                  display: true,
                  text: "Success Rate Over Time",
                },
                tooltip: {
                  callbacks: {
                    afterBody: (context: any) => {
                      const point = data[context[0].dataIndex];
                      return `Total: ${point.total}, Successful: ${point.successful}`;
                    },
                  },
                },
              },
              scales: {
                x: {
                  title: {
                    display: true,
                    text: "Week",
                  },
                },
                y: {
                  beginAtZero: true,
                  max: 100,
                  title: {
                    display: true,
                    text: "Success Rate (%)",
                  },
                },
              },
            },
          },
          config,
          metadata: {
            generated: new Date(),
            dataPoints: data.length,
            timeRange,
            filters: { type: "success_trends" },
          },
        };
      }
    
      /**
       * Generate knowledge graph network visualization
       */
      async generateKnowledgeGraphVisualization(
        config: Partial<VisualizationConfig>,
      ): Promise<ChartData> {
        const allNodes = await this.knowledgeGraph.getAllNodes();
        const allEdges = await this.knowledgeGraph.getAllEdges();
    
        // Prepare network data
        const networkData: NetworkVisualization = {
          nodes: allNodes.map((node) => ({
            id: node.id,
            label: node.label || node.id.slice(0, 10),
            group: node.type,
            size: Math.max(10, Math.min(30, (node.weight || 1) * 10)),
            color: this.getColorForNodeType(node.type, config.colorScheme),
            metadata: node.properties,
          })),
          edges: allEdges.map((edge) => ({
            source: edge.source,
            target: edge.target,
            weight: edge.weight,
            type: edge.type,
            color: this.getColorForEdgeType(edge.type, config.colorScheme),
            metadata: edge.properties,
          })),
          layout: "force",
          clustering: true,
        };
    
        return {
          type: "network",
          title: "Knowledge Graph Network",
          description:
            "Interactive network visualization of memory relationships and connections",
          data: networkData,
          config,
          metadata: {
            generated: new Date(),
            dataPoints: allNodes.length + allEdges.length,
            filters: { type: "knowledge_graph" },
          },
        };
      }
    
      /**
       * Generate learning patterns heatmap
       */
      async generateLearningPatternsHeatmap(
        config: Partial<VisualizationConfig>,
      ): Promise<ChartData> {
        const patterns = await this.learningSystem.getPatterns();
    
        // Create correlation matrix between different pattern dimensions
        const frameworks = [
          ...new Set(
            patterns
              .flatMap((p) => p.metadata.technologies || [])
              .filter(
                (t) =>
                  t.includes("framework") ||
                  t.includes("js") ||
                  t.includes("react") ||
                  t.includes("vue"),
              ),
          ),
        ];
        const languages = [
          ...new Set(
            patterns
              .flatMap((p) => p.metadata.technologies || [])
              .filter((t) => !t.includes("framework")),
          ),
        ];
    
        const heatmapData: number[][] = [];
        const labels = { x: frameworks, y: languages };
    
        for (const language of languages) {
          const row: number[] = [];
          for (const framework of frameworks) {
            // Calculate correlation/co-occurrence
            const langPatterns = patterns.filter(
              (p) => p.metadata.technologies?.includes(language),
            );
            const frameworkPatterns = patterns.filter(
              (p) => p.metadata.technologies?.includes(framework),
            );
            const bothPatterns = patterns.filter(
              (p) =>
                p.metadata.technologies?.includes(language) &&
                p.metadata.technologies?.includes(framework),
            );
    
            const correlation =
              langPatterns.length > 0 && frameworkPatterns.length > 0
                ? bothPatterns.length /
                  Math.min(langPatterns.length, frameworkPatterns.length)
                : 0;
    
            row.push(correlation);
          }
          heatmapData.push(row);
        }
    
        const heatmap: HeatmapVisualization = {
          data: heatmapData,
          labels,
          colorScale: {
            min: 0,
            max: 1,
            colors: ["#F3F4F6", "#93C5FD", "#3B82F6", "#1D4ED8", "#1E3A8A"],
          },
          title: "Language-Framework Learning Patterns",
          description:
            "Correlation matrix showing relationships between programming languages and frameworks in learning patterns",
        };
    
        return {
          type: "heatmap",
          title: "Learning Patterns Heatmap",
          description:
            "Visualization of learning pattern correlations across languages and frameworks",
          data: heatmap,
          config,
          metadata: {
            generated: new Date(),
            dataPoints: patterns.length,
            filters: { type: "learning_patterns" },
          },
        };
      }
    
      /**
       * Generate temporal patterns chart
       */
      async generateTemporalPatternsChart(
        timeRange: { start: Date; end: Date },
        config: Partial<VisualizationConfig>,
      ): Promise<ChartData> {
        const patterns = await this.temporalAnalysis.analyzeTemporalPatterns({
          granularity: "day",
          aggregation: "count",
          timeRange: {
            start: timeRange.start,
            end: timeRange.end,
            duration: timeRange.end.getTime() - timeRange.start.getTime(),
            label: "Analysis Period",
          },
        });
    
        // Prepare data for different pattern types
        const patternData = patterns.map((pattern) => ({
          type: pattern.type,
          confidence: pattern.confidence,
          description: pattern.description,
          dataPoints: pattern.dataPoints?.length || 0,
        }));
    
        const data = {
          labels: patternData.map((p) => p.type),
          datasets: [
            {
              label: "Pattern Confidence",
              data: patternData.map((p) => p.confidence * 100),
              backgroundColor: config.colorScheme || this.defaultConfig.colorScheme,
              borderColor:
                config.colorScheme?.map((c) => this.darkenColor(c)) ||
                this.defaultConfig.colorScheme.map((c) => this.darkenColor(c)),
              borderWidth: 2,
            },
          ],
        };
    
        return {
          type: "bar",
          title: "Temporal Patterns Analysis",
          description:
            "Confidence levels of detected temporal patterns in memory activity",
          data: {
            ...data,
            options: {
              responsive: config.responsive,
              plugins: {
                title: {
                  display: true,
                  text: "Detected Temporal Patterns",
                },
                tooltip: {
                  callbacks: {
                    afterBody: (context: any) => {
                      const pattern = patternData[context[0].dataIndex];
                      return pattern.description;
                    },
                  },
                },
              },
              scales: {
                y: {
                  beginAtZero: true,
                  max: 100,
                  title: {
                    display: true,
                    text: "Confidence (%)",
                  },
                },
                x: {
                  title: {
                    display: true,
                    text: "Pattern Type",
                  },
                },
              },
            },
          },
          config,
          metadata: {
            generated: new Date(),
            dataPoints: patterns.length,
            timeRange,
            filters: { type: "temporal_patterns" },
          },
        };
      }
    
      /**
       * Generate project correlation matrix
       */
      async generateProjectCorrelationMatrix(
        timeRange: { start: Date; end: Date },
        config: Partial<VisualizationConfig>,
      ): Promise<ChartData> {
        const entries = await this.getEntriesInTimeRange(timeRange);
    
        // Extract unique projects
        const projects = [
          ...new Set(
            entries
              .map((e) => e.data.projectPath || e.data.projectId || "Unknown")
              .filter((p) => p !== "Unknown"),
          ),
        ].slice(0, 10); // Limit to top 10
    
        // Calculate correlation matrix
        const correlationMatrix: number[][] = [];
    
        for (const project1 of projects) {
          const row: number[] = [];
          for (const project2 of projects) {
            if (project1 === project2) {
              row.push(1.0);
            } else {
              const correlation = this.calculateProjectCorrelation(
                entries,
                project1,
                project2,
              );
              row.push(correlation);
            }
          }
          correlationMatrix.push(row);
        }
    
        const heatmap: HeatmapVisualization = {
          data: correlationMatrix,
          labels: { x: projects, y: projects },
          colorScale: {
            min: -1,
            max: 1,
            colors: ["#EF4444", "#F59E0B", "#F3F4F6", "#10B981", "#059669"],
          },
          title: "Project Correlation Matrix",
          description:
            "Correlation matrix showing relationships between different projects based on memory patterns",
        };
    
        return {
          type: "heatmap",
          title: "Project Correlations",
          description:
            "Visualization of correlations between different projects in the memory system",
          data: heatmap,
          config,
          metadata: {
            generated: new Date(),
            dataPoints: projects.length * projects.length,
            timeRange,
            filters: { type: "project_correlation" },
          },
        };
      }
    
      /**
       * Generate custom visualization
       */
      async generateCustomVisualization(
        type: ChartData["type"],
        query: {
          filters?: Record<string, any>;
          timeRange?: { start: Date; end: Date };
          aggregation?: string;
          groupBy?: string;
        },
        config?: Partial<VisualizationConfig>,
      ): Promise<ChartData> {
        const activeConfig = { ...this.defaultConfig, ...config };
        const timeRange = query.timeRange || this.getDefaultTimeRange();
    
        let entries = await this.getEntriesInTimeRange(timeRange);
    
        // Apply filters
        if (query.filters) {
          entries = this.applyFilters(entries, query.filters);
        }
    
        switch (type) {
          case "timeline":
            return this.generateTimelineVisualization(entries, query, activeConfig);
          case "scatter":
            return this.generateScatterPlot(entries, query, activeConfig);
          case "treemap":
            return this.generateTreemapVisualization(entries, query, activeConfig);
          case "sankey":
            return this.generateSankeyDiagram(entries, query, activeConfig);
          default:
            throw new Error(`Unsupported visualization type: ${type}`);
        }
      }
    
      /**
       * Export visualization to specified format
       */
      async exportVisualization(
        chartData: ChartData,
        format: "svg" | "png" | "json" | "html" = "json",
        options?: {
          filename?: string;
          quality?: number;
          width?: number;
          height?: number;
        },
      ): Promise<string | Buffer> {
        this.emit("export_started", { type: chartData.type, format });
    
        try {
          switch (format) {
            case "json":
              return JSON.stringify(chartData, null, 2);
    
            case "html":
              return this.generateHTMLVisualization(chartData, options);
    
            case "svg":
              return this.generateSVGVisualization(chartData, options);
    
            case "png":
              // This would require a rendering library like Puppeteer
              throw new Error(
                "PNG export requires additional rendering capabilities",
              );
    
            default:
              throw new Error(`Unsupported export format: ${format}`);
          }
        } catch (error) {
          this.emit("export_error", {
            error: error instanceof Error ? error.message : String(error),
          });
          throw error;
        }
      }
    
      /**
       * Helper methods
       */
      private async getEntriesInTimeRange(timeRange: {
        start: Date;
        end: Date;
      }): Promise<MemoryEntry[]> {
        const allEntries = await this.storage.getAll();
        return allEntries.filter((entry) => {
          const entryDate = new Date(entry.timestamp);
          return entryDate >= timeRange.start && entryDate <= timeRange.end;
        });
      }
    
      private getDefaultTimeRange(): { start: Date; end: Date } {
        const end = new Date();
        const start = new Date(end.getTime() - 30 * 24 * 60 * 60 * 1000); // 30 days ago
        return { start, end };
      }
    
      private getWeekKey(date: Date): string {
        const year = date.getFullYear();
        const week = this.getWeekNumber(date);
        return `${year}-W${week.toString().padStart(2, "0")}`;
      }
    
      private getWeekNumber(date: Date): number {
        const d = new Date(
          Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()),
        );
        const dayNum = d.getUTCDay() || 7;
        d.setUTCDate(d.getUTCDate() + 4 - dayNum);
        const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
        return Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
      }
    
      private getColorForNodeType(type: string, colorScheme?: string[]): string {
        const colors = colorScheme || this.defaultConfig.colorScheme;
        const index = type.charCodeAt(0) % colors.length;
        return colors[index];
      }
    
      private getColorForEdgeType(type: string, colorScheme?: string[]): string {
        const colors = colorScheme || this.defaultConfig.colorScheme;
        const typeColors: Record<string, string> = {
          similarity: colors[0],
          dependency: colors[1],
          temporal: colors[2],
          causal: colors[3],
        };
        return typeColors[type] || colors[4];
      }
    
      private darkenColor(color: string): string {
        // Simple color darkening - in production, use a proper color library
        if (color.startsWith("#")) {
          const hex = color.slice(1);
          const num = parseInt(hex, 16);
          const r = Math.max(0, (num >> 16) - 40);
          const g = Math.max(0, ((num >> 8) & 0x00ff) - 40);
          const b = Math.max(0, (num & 0x0000ff) - 40);
          return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, "0")}`;
        }
        return color;
      }
    
      private calculateProjectCorrelation(
        entries: MemoryEntry[],
        project1: string,
        project2: string,
      ): number {
        const entries1 = entries.filter(
          (e) =>
            e.data.projectPath?.includes(project1) || e.data.projectId === project1,
        );
        const entries2 = entries.filter(
          (e) =>
            e.data.projectPath?.includes(project2) || e.data.projectId === project2,
        );
    
        if (entries1.length === 0 || entries2.length === 0) return 0;
    
        // Simple correlation based on shared characteristics
        let sharedFeatures = 0;
        let totalFeatures = 0;
    
        // Compare languages
        const lang1 = new Set(entries1.map((e) => e.data.language).filter(Boolean));
        const lang2 = new Set(entries2.map((e) => e.data.language).filter(Boolean));
        const sharedLangs = new Set([...lang1].filter((l) => lang2.has(l)));
        sharedFeatures += sharedLangs.size;
        totalFeatures += new Set([...lang1, ...lang2]).size;
    
        // Compare frameworks
        const fw1 = new Set(entries1.map((e) => e.data.framework).filter(Boolean));
        const fw2 = new Set(entries2.map((e) => e.data.framework).filter(Boolean));
        const sharedFws = new Set([...fw1].filter((f) => fw2.has(f)));
        sharedFeatures += sharedFws.size;
        totalFeatures += new Set([...fw1, ...fw2]).size;
    
        return totalFeatures > 0 ? sharedFeatures / totalFeatures : 0;
      }
    
      private applyFilters(
        entries: MemoryEntry[],
        filters: Record<string, any>,
      ): MemoryEntry[] {
        return entries.filter((entry) => {
          for (const [key, value] of Object.entries(filters)) {
            switch (key) {
              case "type":
                if (Array.isArray(value) && !value.includes(entry.type))
                  return false;
                if (typeof value === "string" && entry.type !== value) return false;
                break;
              case "outcome":
                if (entry.data.outcome !== value) return false;
                break;
              case "language":
                if (entry.data.language !== value) return false;
                break;
              case "framework":
                if (entry.data.framework !== value) return false;
                break;
              case "project":
                if (
                  !entry.data.projectPath?.includes(value) &&
                  entry.data.projectId !== value
                ) {
                  return false;
                }
                break;
              case "tags":
                if (
                  Array.isArray(value) &&
                  !value.some((tag) => entry.tags?.includes(tag))
                ) {
                  return false;
                }
                break;
            }
          }
          return true;
        });
      }
    
      private async generateKeyInsights(
        entries: MemoryEntry[],
        timeRange: { start: Date; end: Date },
      ): Promise<string[]> {
        const insights: string[] = [];
    
        // Activity insight
        const dailyAverage =
          entries.length /
          Math.max(
            1,
            Math.ceil(
              (timeRange.end.getTime() - timeRange.start.getTime()) /
                (24 * 60 * 60 * 1000),
            ),
          );
        insights.push(`Average ${dailyAverage.toFixed(1)} entries per day`);
    
        // Success rate insight
        const successful = entries.filter(
          (e) => e.data.outcome === "success" || e.data.success === true,
        ).length;
        const successRate =
          entries.length > 0 ? (successful / entries.length) * 100 : 0;
        insights.push(`${successRate.toFixed(1)}% success rate`);
    
        // Most common type
        const typeCounts = new Map<string, number>();
        entries.forEach((e) =>
          typeCounts.set(e.type, (typeCounts.get(e.type) || 0) + 1),
        );
        const mostCommonType = Array.from(typeCounts.entries()).sort(
          ([, a], [, b]) => b - a,
        )[0];
        if (mostCommonType) {
          insights.push(
            `Most common activity: ${mostCommonType[0]} (${mostCommonType[1]} entries)`,
          );
        }
    
        // Growth trend
        const midpoint = new Date(
          (timeRange.start.getTime() + timeRange.end.getTime()) / 2,
        );
        const firstHalf = entries.filter(
          (e) => new Date(e.timestamp) < midpoint,
        ).length;
        const secondHalf = entries.filter(
          (e) => new Date(e.timestamp) >= midpoint,
        ).length;
        if (firstHalf > 0) {
          const growthRate = ((secondHalf - firstHalf) / firstHalf) * 100;
          insights.push(
            `Activity ${growthRate >= 0 ? "increased" : "decreased"} by ${Math.abs(
              growthRate,
            ).toFixed(1)}%`,
          );
        }
    
        return insights.slice(0, 5); // Return top 5 insights
      }
    
      private async calculateSystemHealthScore(
        entries: MemoryEntry[],
      ): Promise<number> {
        let score = 0;
    
        // Activity level (0-25 points)
        const recentEntries = entries.filter(
          (e) =>
            new Date(e.timestamp) > new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
        );
        score += Math.min(25, recentEntries.length * 2);
    
        // Success rate (0-25 points)
        const successful = entries.filter(
          (e) => e.data.outcome === "success" || e.data.success === true,
        ).length;
        const successRate = entries.length > 0 ? successful / entries.length : 0;
        score += successRate * 25;
    
        // Diversity (0-25 points)
        const uniqueTypes = new Set(entries.map((e) => e.type)).size;
        score += Math.min(25, uniqueTypes * 3);
    
        // Consistency (0-25 points)
        if (entries.length >= 7) {
          const dailyActivities = new Map<string, number>();
          entries.forEach((e) => {
            const day = e.timestamp.slice(0, 10);
            dailyActivities.set(day, (dailyActivities.get(day) || 0) + 1);
          });
    
          const values = Array.from(dailyActivities.values());
          const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
          const variance =
            values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) /
            values.length;
          const consistency =
            mean > 0 ? Math.max(0, 1 - Math.sqrt(variance) / mean) : 0;
          score += consistency * 25;
        }
    
        return Math.round(Math.min(100, score));
      }
    
      private generateTimelineVisualization(
        entries: MemoryEntry[],
        query: any,
        config: VisualizationConfig,
      ): ChartData {
        const events = entries.map((entry) => ({
          id: entry.id,
          timestamp: new Date(entry.timestamp),
          title: entry.type,
          description: entry.data.description || `${entry.type} entry`,
          type: entry.type,
          importance: entry.data.outcome === "success" ? 1 : 0.5,
          color: this.getColorForNodeType(entry.type, config.colorScheme),
          metadata: entry.data,
        }));
    
        const timelineData: TimelineVisualization = {
          events,
          timeRange: {
            start: new Date(Math.min(...events.map((e) => e.timestamp.getTime()))),
            end: new Date(Math.max(...events.map((e) => e.timestamp.getTime()))),
          },
          granularity: "day",
          groupBy: query.groupBy,
        };
    
        return {
          type: "timeline",
          title: "Memory Activity Timeline",
          description: "Chronological timeline of memory system activities",
          data: timelineData,
          config,
          metadata: {
            generated: new Date(),
            dataPoints: events.length,
            filters: query.filters,
          },
        };
      }
    
      private generateScatterPlot(
        entries: MemoryEntry[],
        query: any,
        config: VisualizationConfig,
      ): ChartData {
        // Create scatter plot data based on timestamp vs some metric
        const data = entries.map((entry) => ({
          x: new Date(entry.timestamp).getTime(),
          y: entry.data.duration || entry.data.complexity || Math.random(), // Use available metric
          color: this.getColorForNodeType(entry.type, config.colorScheme),
          metadata: entry,
        }));
    
        return {
          type: "scatter",
          title: "Memory Activity Scatter Plot",
          description: "Scatter plot visualization of memory activities",
          data: {
            datasets: [
              {
                label: "Activities",
                data: data,
                backgroundColor: data.map((d) => d.color),
              },
            ],
          },
          config,
          metadata: {
            generated: new Date(),
            dataPoints: data.length,
            filters: query.filters,
          },
        };
      }
    
      private generateTreemapVisualization(
        entries: MemoryEntry[],
        query: any,
        config: VisualizationConfig,
      ): ChartData {
        // Group entries by type and project for treemap
        const hierarchy = new Map<string, Map<string, number>>();
    
        for (const entry of entries) {
          const type = entry.type;
          const project =
            entry.data.projectPath || entry.data.projectId || "Unknown";
    
          if (!hierarchy.has(type)) {
            hierarchy.set(type, new Map());
          }
          hierarchy
            .get(type)!
            .set(project, (hierarchy.get(type)!.get(project) || 0) + 1);
        }
    
        // Convert to treemap format
        const treemapData = Array.from(hierarchy.entries()).map(
          ([type, projects]) => ({
            name: type,
            value: Array.from(projects.values()).reduce((sum, val) => sum + val, 0),
            children: Array.from(projects.entries()).map(([project, count]) => ({
              name: project,
              value: count,
            })),
          }),
        );
    
        return {
          type: "treemap",
          title: "Memory Type Hierarchy",
          description: "Hierarchical treemap of memory entries by type and project",
          data: treemapData,
          config,
          metadata: {
            generated: new Date(),
            dataPoints: entries.length,
            filters: query.filters,
          },
        };
      }
    
      private generateSankeyDiagram(
        entries: MemoryEntry[],
        query: any,
        config: VisualizationConfig,
      ): ChartData {
        // Create flow data from entry types to outcomes
        const flows = new Map<string, Map<string, number>>();
    
        for (const entry of entries) {
          const source = entry.type;
          const target =
            entry.data.outcome || (entry.data.success ? "success" : "unknown");
    
          if (!flows.has(source)) {
            flows.set(source, new Map());
          }
          flows.get(source)!.set(target, (flows.get(source)!.get(target) || 0) + 1);
        }
    
        // Convert to Sankey format
        const nodes: string[] = [];
        const links: Array<{ source: number; target: number; value: number }> = [];
    
        // Collect all unique nodes
        const sources = Array.from(flows.keys());
        const targets = new Set<string>();
        flows.forEach((targetMap) => {
          targetMap.forEach((_, target) => targets.add(target));
        });
    
        nodes.push(
          ...sources,
          ...Array.from(targets).filter((t) => !sources.includes(t)),
        );
    
        // Create links
        flows.forEach((targetMap, source) => {
          targetMap.forEach((value, target) => {
            const sourceIndex = nodes.indexOf(source);
            const targetIndex = nodes.indexOf(target);
            if (sourceIndex !== -1 && targetIndex !== -1) {
              links.push({ source: sourceIndex, target: targetIndex, value });
            }
          });
        });
    
        return {
          type: "sankey",
          title: "Memory Flow Diagram",
          description: "Sankey diagram showing flow from memory types to outcomes",
          data: { nodes, links },
          config,
          metadata: {
            generated: new Date(),
            dataPoints: links.length,
            filters: query.filters,
          },
        };
      }
    
      private generateHTMLVisualization(
        chartData: ChartData,
        _options?: any,
      ): string {
        // Generate basic HTML with embedded Chart.js or D3.js
        return `
    <!DOCTYPE html>
    <html>
    <head>
        <title>${chartData.title}</title>
        <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
        <style>
            body { font-family: Arial, sans-serif; margin: 20px; }
            .chart-container { width: 100%; height: 400px; }
            .description { margin-bottom: 20px; color: #666; }
        </style>
    </head>
    <body>
        <h1>${chartData.title}</h1>
        <p class="description">${chartData.description}</p>
        <div class="chart-container">
            <canvas id="chart"></canvas>
        </div>
        <script>
            const ctx = document.getElementById('chart').getContext('2d');
            new Chart(ctx, ${JSON.stringify(chartData.data)});
        </script>
    </body>
    </html>`;
      }
    
      private generateSVGVisualization(
        chartData: ChartData,
        options?: any,
      ): string {
        // Generate basic SVG - in production, use a proper chart library
        const width = options?.width || 800;
        const height = options?.height || 600;
    
        return `
    <svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
        <rect width="100%" height="100%" fill="white"/>
        <text x="50%" y="30" text-anchor="middle" font-size="18" font-weight="bold">
            ${chartData.title}
        </text>
        <text x="50%" y="50" text-anchor="middle" font-size="14" fill="#666">
            ${chartData.description}
        </text>
        <!-- Chart data would be rendered here -->
        <text x="50%" y="${
          height / 2
        }" text-anchor="middle" font-size="12" fill="#999">
            Chart visualization (${chartData.metadata.dataPoints} data points)
        </text>
    </svg>`;
      }
    }
  • Type definitions for visualization configuration used in tool inputs and outputs.
    export interface VisualizationConfig {
      width: number;
      height: number;
      theme: "light" | "dark" | "auto";
      colorScheme: string[];
      interactive: boolean;
      exportFormat: "svg" | "png" | "json" | "html";
      responsive: boolean;
    }
  • Export of MemoryVisualizationSystem class and related types from visualization module.
      MemoryVisualizationSystem,
      type VisualizationConfig,
      type ChartData,
      type DashboardData,
      type NetworkVisualization,
    } from "./visualization.js";

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tosin2013/documcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server