Skip to main content
Glama

get_statistics

Analyze journal entry patterns and trends by generating statistics on time periods and top tags to track writing habits and content themes.

Instructions

Get journal statistics and analytics

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
journalNoJournal name (uses current/default if not specified)
timeGroupingNoGroup statistics by time period
includeTopTagsNoInclude top tags in statistics

Implementation Reference

  • The core handler function for the 'get_statistics' tool. It executes a jrnl stats command, parses the JSON output, computes total entries, total words, average words per entry, optional time-grouped statistics, and top 10 tags.
    export async function getStatistics( journal: string | undefined, timeGrouping: string | undefined, includeTopTags: boolean, executor: JrnlExecutor, ): Promise<{ statistics: JournalStatistics }> { const command = buildStatsCommand(journal); const result = await executor.execute(command); try { const data = JSON.parse(result); const entries = data.entries || []; // Calculate basic statistics const totalEntries = entries.length; const totalWords = entries.reduce((sum: number, entry: any) => { const bodyWords = entry.body ? entry.body.split(/\s+/).length : 0; const titleWords = entry.title ? entry.title.split(/\s+/).length : 0; return sum + bodyWords + titleWords; }, 0); const averageWordsPerEntry = totalEntries > 0 ? Math.round(totalWords / totalEntries) : 0; const statistics: JournalStatistics = { totalEntries, totalWords, averageWordsPerEntry, }; // Time grouping statistics if (timeGrouping) { const grouping = parseTimeGrouping(timeGrouping); const grouped = groupEntriesByTime(entries, grouping); statistics.timeGrouping = Object.entries(grouped).map( ([period, entries]) => { const wordCount = entries.reduce((sum: number, entry: any) => { const bodyWords = entry.body ? entry.body.split(/\s+/).length : 0; const titleWords = entry.title ? entry.title.split(/\s+/).length : 0; return sum + bodyWords + titleWords; }, 0); return { period, entryCount: entries.length, wordCount, }; }, ); } // Top tags if (includeTopTags) { const tagCounts = new Map<string, number>(); entries.forEach((entry: any) => { if (entry.tags) { entry.tags.forEach((tag: string) => { const normalizedTag = tag.startsWith("@") ? tag : `@${tag}`; tagCounts.set( normalizedTag, (tagCounts.get(normalizedTag) || 0) + 1, ); }); } }); statistics.topTags = Array.from(tagCounts.entries()) .map(([tag, count]) => ({ tag, count })) .sort((a, b) => b.count - a.count) .slice(0, 10); // Top 10 tags } return { statistics }; } catch (error) { throw new Error(`Failed to calculate statistics: ${error}`); } }
  • TypeScript interfaces defining the structure of the statistics output returned by the get_statistics tool.
    export interface TimeGroupStats { period: string; entryCount: number; wordCount: number; } export interface JournalStatistics { totalEntries: number; totalWords: number; averageWordsPerEntry: number; timeGrouping?: TimeGroupStats[]; topTags?: Array<{ tag: string; count: number }>; }
  • src/index.ts:98-119 (registration)
    Tool registration in the ListTools handler, defining the name, description, and input schema for get_statistics.
    { name: "get_statistics", description: "Get journal statistics and analytics", inputSchema: { type: "object", properties: { journal: { type: "string", description: "Journal name (uses current/default if not specified)", }, timeGrouping: { type: "string", enum: ["day", "week", "month", "year"], description: "Group statistics by time period", }, includeTopTags: { type: "boolean", description: "Include top tags in statistics", }, }, }, },
  • src/index.ts:196-217 (registration)
    Dispatch case in the CallTool handler that invokes the getStatistics function with parsed arguments.
    case "get_statistics": return { content: [ { type: "text", text: JSON.stringify( await getStatistics( journal, typeof args?.timeGrouping === "string" ? args.timeGrouping : undefined, typeof args?.includeTopTags === "boolean" ? args.includeTopTags : true, executor, ), null, 2, ), }, ], };
  • Helper functions for grouping entries by time periods (daily, weekly, monthly, yearly) and calculating week numbers, used by the getStatistics handler.
    function groupEntriesByTime( entries: any[], grouping: string, ): Record<string, any[]> { const grouped: Record<string, any[]> = {}; entries.forEach((entry: any) => { const date = new Date(entry.date); let key: string; switch (grouping) { case "daily": key = date.toISOString().split("T")[0]; break; case "weekly": { // Get week number const weekNum = getWeekNumber(date); key = `${date.getFullYear()}-W${weekNum}`; break; } case "monthly": key = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`; break; case "yearly": key = date.getFullYear().toString(); break; default: key = date.toISOString().split("T")[0]; } if (!grouped[key]) { grouped[key] = []; } grouped[key].push(entry); }); return grouped; } function 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); }

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/yostos/jrnl-mcp'

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