Skip to main content
Glama

COA Goldfish MCP

by anortham
display-handler.tsโ€ข6.96 kB
/** * Display Handler for VS Code Bridge * * Sends structured Goldfish data to VS Code for visualization */ import { GoldfishMemory } from '../types/index.js'; export interface VSCodeBridgeClient { isConnected: boolean; sendVisualization(type: string, data: any, hint?: any): Promise<void>; display(data: any, options?: any): Promise<void>; } /** * Handles sending Goldfish data to VS Code for display */ export class GoldfishDisplayHandler { private bridge: VSCodeBridgeClient | null = null; constructor(bridge?: VSCodeBridgeClient) { this.bridge = bridge || null; } /** * Check if VS Code bridge is available */ get isAvailable(): boolean { return this.bridge !== null && this.bridge.isConnected; } /** * Display timeline in VS Code */ async displayTimeline(memories: GoldfishMemory[], title?: string): Promise<void> { if (!this.isAvailable) return; try { // Transform memories to timeline format expected by VS Code bridge const timelineData = { items: memories.map(memory => ({ id: memory.id, timestamp: memory.timestamp, title: memory.content, type: memory.type, tags: memory.tags || [], metadata: memory.metadata, workspace: memory.workspace })), totalCount: memories.length, timeRange: memories.length > 0 ? { start: memories[memories.length - 1]?.timestamp || '', end: memories[0]?.timestamp || '' } : null }; await this.bridge!.sendVisualization( 'timeline', timelineData, { interactive: true, consolidateTabs: true, title: title || 'Goldfish Timeline' } ); } catch (error) { console.warn('Failed to display timeline in VS Code:', error); } } /** * Display memory statistics as chart */ async displayStats(memories: GoldfishMemory[]): Promise<void> { if (!this.isAvailable) return; try { // Calculate statistics const typeDistribution = this.calculateTypeDistribution(memories); const workspaceDistribution = this.calculateWorkspaceDistribution(memories); // Show type distribution as data grid await this.bridge!.sendVisualization( 'data-grid', { columns: ['Type', 'Count'], rows: Object.entries(typeDistribution).map(([type, count]) => [type, count]) }, { title: 'Memory Types Distribution', interactive: true } ); // Show workspace distribution as data grid if (Object.keys(workspaceDistribution).length > 1) { await this.bridge!.sendVisualization( 'data-grid', { columns: ['Workspace', 'Count'], rows: Object.entries(workspaceDistribution).map(([ws, count]) => [ws, count]) }, { title: 'Memories by Workspace', interactive: true } ); } } catch (error) { console.warn('Failed to display stats in VS Code:', error); } } /** * Display TODO lists as data grid */ async displayTodos(todos: any[]): Promise<void> { if (!this.isAvailable) return; try { // Transform todos to table format const todoData = { columns: ['Task', 'Status', 'Priority', 'List'], rows: todos.flatMap(list => list.items.map((item: any) => [ item.task, item.status, item.priority || 'normal', list.title ]) ) }; await this.bridge!.sendVisualization( 'data-grid', todoData, { title: 'Active TODO Items', interactive: true, sortable: true } ); } catch (error) { console.warn('Failed to display TODOs in VS Code:', error); } } /** * Display search results */ async displaySearchResults( results: GoldfishMemory[], query: string ): Promise<void> { if (!this.isAvailable) return; try { // Send as timeline with search context await this.displayTimeline(results, `Search: "${query}"`); // Also show tag analysis if results have tags const tagAnalysis = this.analyzeTags(results); if (tagAnalysis.length > 0) { await this.bridge!.sendVisualization( 'data-grid', { columns: ['Tag', 'Count', 'Percentage'], rows: tagAnalysis }, { title: 'Tag Analysis', interactive: false } ); } } catch (error) { console.warn('Failed to display search results in VS Code:', error); } } /** * Display session summary */ async displaySessionSummary(sessionData: any): Promise<void> { if (!this.isAvailable) return; try { // Create summary data structure const summaryData = { sessionId: sessionData.sessionId, duration: sessionData.duration, checkpoints: sessionData.checkpointCount, activeFiles: sessionData.activeFiles || [], highlights: sessionData.highlights || [], workspace: sessionData.workspace }; // Show as JSON tree await this.bridge!.sendVisualization( 'json-tree', summaryData, { title: 'Session Summary', interactive: false } ); } catch (error) { console.warn('Failed to display session summary in VS Code:', error); } } // Helper methods private calculateTypeDistribution(memories: GoldfishMemory[]): Record<string, number> { const distribution: Record<string, number> = {}; memories.forEach(memory => { distribution[memory.type] = (distribution[memory.type] || 0) + 1; }); return distribution; } private calculateWorkspaceDistribution(memories: GoldfishMemory[]): Record<string, number> { const distribution: Record<string, number> = {}; memories.forEach(memory => { const workspace = memory.workspace || 'default'; distribution[workspace] = (distribution[workspace] || 0) + 1; }); return distribution; } private analyzeTags(memories: GoldfishMemory[]): Array<[string, number, string]> { const tagCounts: Record<string, number> = {}; let totalTags = 0; memories.forEach(memory => { if (memory.tags) { memory.tags.forEach(tag => { tagCounts[tag] = (tagCounts[tag] || 0) + 1; totalTags++; }); } }); // Convert to array and sort by count return Object.entries(tagCounts) .sort((a, b) => b[1] - a[1]) .slice(0, 10) // Top 10 tags .map(([tag, count]) => [ tag, count, `${((count / totalTags) * 100).toFixed(1)}%` ]); } }

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/anortham/coa-goldfish-mcp'

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