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";
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure but offers minimal information. It doesn't describe what 'memory data' refers to, what format the visualizations take, whether this is a read-only or write operation, performance characteristics, or any side effects. The description is too vague to guide the agent's expectations about tool behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise at just 5 words, with no wasted language. It's front-loaded with the core functionality and contains no unnecessary elaboration. While this conciseness comes at the cost of completeness, the description itself is structurally efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (3 parameters with nested objects, no output schema, and no annotations), the description is completely inadequate. It doesn't explain what memory data is visualized, what format the visualizations take, how parameters affect results, or what the tool returns. For a visualization tool with multiple configuration options, this minimal description leaves the agent with insufficient context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage and 3 parameters, the description provides no information about any parameters. It doesn't explain what 'visualizationType' options mean, what 'options' or 'customVisualization' objects should contain, or how these parameters affect the output. The description fails to compensate for the complete lack of schema documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Generate visual representations') and the target data ('memory data'), which provides a specific verb+resource combination. However, it doesn't differentiate this tool from sibling tools like 'memory_insights' or 'memory_intelligent_analysis' that might also involve visualization or analysis of memory data.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. There's no mention of prerequisites, appropriate contexts, or comparison to sibling tools like 'memory_insights' or 'memory_temporal_analysis' that might offer overlapping functionality. The agent receives no usage direction.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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