Skip to main content
Glama

get_analytics

Retrieve Anki study analytics including deck statistics, review history, and card performance data for tracking learning progress.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
scopeYesAnalytics scope
deckNameNoDeck name for deck-specific stats
cardIdsNoCard IDs for detailed analysis
startTimestampNoStart timestamp for review history
wholeCollectionNoGet whole collection stats vs current deck

Implementation Reference

  • Main execution logic for get_analytics tool. Handles various analytics scopes (deck_stats, collection_stats, reviews_by_day, etc.) by invoking AnkiConnect API methods via ankiClient and returning formatted results.
    async ({ scope, deckName, cardIds, startTimestamp, wholeCollection }) => { try { switch (scope) { case 'deck_stats': { if (!deckName) { throw new Error('deck_stats requires deckName'); } const stats = await ankiClient.deck.getDeckStats({ decks: [deckName] }); return { content: [ { type: 'text', text: `๐Ÿ“Š Statistics for "${deckName}":\n${JSON.stringify(stats, null, 2)}`, }, ], }; } case 'collection_stats': { const statsHTML = await ankiClient.statistic.getCollectionStatsHTML({ wholeCollection: wholeCollection ?? true, }); return { content: [ { type: 'text', text: `๐Ÿ“Š Collection Statistics (${wholeCollection ? 'all decks' : 'current deck'}):\n${statsHTML}`, }, ], }; } case 'reviews_by_day': { const reviewsByDay = await ankiClient.statistic.getNumCardsReviewedByDay(); return { content: [ { type: 'text', text: `๐Ÿ“Š Reviews by day (${reviewsByDay.length} days):\n${JSON.stringify(reviewsByDay, null, 2)}`, }, ], }; } case 'reviews_today': { const count = await ankiClient.statistic.getNumCardsReviewedToday(); return { content: [ { type: 'text', text: `๐Ÿ“Š Cards reviewed today: ${count}`, }, ], }; } case 'card_reviews': { if (!deckName || startTimestamp === undefined) { throw new Error('card_reviews requires deckName and startTimestamp'); } const reviews = await ankiClient.statistic.cardReviews({ deck: deckName, startID: startTimestamp, }); return { content: [ { type: 'text', text: `๐Ÿ“Š Found ${reviews.length} reviews for "${deckName}" after ${startTimestamp}:\n${JSON.stringify(reviews, null, 2)}`, }, ], }; } case 'card_details': { if (!cardIds || cardIds.length === 0) { throw new Error('card_details requires cardIds'); } const [easeFactors, intervals] = await Promise.all([ ankiClient.card.getEaseFactors({ cards: cardIds }), ankiClient.card.getIntervals({ cards: cardIds, complete: false }), ]); const details = cardIds.map((id, idx) => ({ cardId: id, easeFactor: easeFactors[idx], currentInterval: intervals[idx], })); return { content: [ { type: 'text', text: `๐Ÿ“Š Card details:\n${JSON.stringify(details, null, 2)}`, }, ], }; } default: throw new Error(`Unknown scope: ${scope}`); } } catch (error) { throw new Error( `get_analytics failed: ${error instanceof Error ? error.message : String(error)}` ); } } );
  • Zod schema defining input parameters for get_analytics: scope (required enum), optional deckName, cardIds, startTimestamp, wholeCollection.
    { scope: z .enum([ 'deck_stats', 'collection_stats', 'reviews_by_day', 'reviews_today', 'card_reviews', 'card_details', ]) .describe('Analytics scope'), deckName: z.string().optional().describe('Deck name for deck-specific stats'), cardIds: z.array(z.number()).optional().describe('Card IDs for detailed analysis'), startTimestamp: z.number().optional().describe('Start timestamp for review history'), wholeCollection: z .boolean() .optional() .describe('Get whole collection stats vs current deck'), },
  • MCP server tool registration call for get_analytics in consolidated tools module.
    server.tool( 'get_analytics', { scope: z .enum([ 'deck_stats', 'collection_stats', 'reviews_by_day', 'reviews_today', 'card_reviews', 'card_details', ]) .describe('Analytics scope'), deckName: z.string().optional().describe('Deck name for deck-specific stats'), cardIds: z.array(z.number()).optional().describe('Card IDs for detailed analysis'), startTimestamp: z.number().optional().describe('Start timestamp for review history'), wholeCollection: z .boolean() .optional() .describe('Get whole collection stats vs current deck'), }, async ({ scope, deckName, cardIds, startTimestamp, wholeCollection }) => { try { switch (scope) { case 'deck_stats': { if (!deckName) { throw new Error('deck_stats requires deckName'); } const stats = await ankiClient.deck.getDeckStats({ decks: [deckName] }); return { content: [ { type: 'text', text: `๐Ÿ“Š Statistics for "${deckName}":\n${JSON.stringify(stats, null, 2)}`, }, ], }; } case 'collection_stats': { const statsHTML = await ankiClient.statistic.getCollectionStatsHTML({ wholeCollection: wholeCollection ?? true, }); return { content: [ { type: 'text', text: `๐Ÿ“Š Collection Statistics (${wholeCollection ? 'all decks' : 'current deck'}):\n${statsHTML}`, }, ], }; } case 'reviews_by_day': { const reviewsByDay = await ankiClient.statistic.getNumCardsReviewedByDay(); return { content: [ { type: 'text', text: `๐Ÿ“Š Reviews by day (${reviewsByDay.length} days):\n${JSON.stringify(reviewsByDay, null, 2)}`, }, ], }; } case 'reviews_today': { const count = await ankiClient.statistic.getNumCardsReviewedToday(); return { content: [ { type: 'text', text: `๐Ÿ“Š Cards reviewed today: ${count}`, }, ], }; } case 'card_reviews': { if (!deckName || startTimestamp === undefined) { throw new Error('card_reviews requires deckName and startTimestamp'); } const reviews = await ankiClient.statistic.cardReviews({ deck: deckName, startID: startTimestamp, }); return { content: [ { type: 'text', text: `๐Ÿ“Š Found ${reviews.length} reviews for "${deckName}" after ${startTimestamp}:\n${JSON.stringify(reviews, null, 2)}`, }, ], }; } case 'card_details': { if (!cardIds || cardIds.length === 0) { throw new Error('card_details requires cardIds'); } const [easeFactors, intervals] = await Promise.all([ ankiClient.card.getEaseFactors({ cards: cardIds }), ankiClient.card.getIntervals({ cards: cardIds, complete: false }), ]); const details = cardIds.map((id, idx) => ({ cardId: id, easeFactor: easeFactors[idx], currentInterval: intervals[idx], })); return { content: [ { type: 'text', text: `๐Ÿ“Š Card details:\n${JSON.stringify(details, null, 2)}`, }, ], }; } default: throw new Error(`Unknown scope: ${scope}`); } } catch (error) { throw new Error( `get_analytics failed: ${error instanceof Error ? error.message : String(error)}` ); } } );

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/arielbk/anki-mcp'

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