MCP server for Granola meeting data. Exposes meetings, transcripts, action items, and agent notes as MCP tools and resources. Supports local SQLite persistence for notes and action item overrides.
The acai server demonstrates a solid foundation with 12 well-organized tools covering a coherent domain (meeting management and transcripts). All tools have basic descriptions and clear semantic purposes. However, the implementation lacks several critical quality patterns: output schemas are not documented in the source code provided, parameter descriptions are minimal (averaging 30-40 chars), tool descriptions are functional but sparse (averaging 60-80 chars, below the production baseline of 194 chars), and error handling guidance is absent. The server implements a clean Ports & Adapters architecture with good separation of concerns, but tool definitions themselves need enrichment to meet production standards. None of the tools show destructive/idempotent hints, and there is no evidence of confirmation patterns for destructive operations (delete_note, complete_action_item).
Output schemas are not documented. The source code does not show documented return types or output structure for any tool. LLMs cannot determine what fields to expect in responses, making downstream tool chaining difficult and forcing extra discovery calls.
Tool descriptions are too brief (35-55 chars average, below 194-char production baseline). Descriptions like 'Get full details for a specific meeting' lack context for LLM tool selection. Should explain WHAT the tool does, WHEN to use it vs similar tools, and what the response structure looks like.
Document the output schema for every tool. Include field names, types, and descriptions in the tool definition (e.g., 'Returns: {meetings: [{id: string, title: string, start: ISO8601, duration_minutes: integer, source: enum[zoom|google_meet|teams|other], transcript_available: boolean}], total_count: integer}'). This enables LLM planning and tool chaining.
Expand tool descriptions to 100-200 characters following the pattern: 'Returns [WHAT]. Use when you need [WHEN]. Related: [SIMILAR_TOOLS].' Example for get_meeting: 'Returns full meeting details including participants, transcript status, and action items. Use when you need complete meeting metadata before accessing transcripts or notes. Related: get_transcript, list_notes.'
Add tool annotations to every tool definition. For read-only tools (list_meetings, get_meeting, get_transcript, search_transcripts, get_action_items, meeting_stats, list_notes, export_embeddings), set readOnlyHint=true. For destructive tools (delete_note), set destructiveHint=true. For idempotent operations (add_note with same content), set idempotentHint=true where applicable.
Implement a confirmation pattern for delete_note. Add an optional 'dry_run' or 'preview' parameter, or create a separate 'preview_note' tool that returns the note content before deletion. This prevents accidental data loss.
Enhance error messages for write operations. When add_note fails due to an invalid meeting_id, return: 'Meeting not found: {meeting_id}. Available meetings from the last 7 days: [list]. Call list_meetings to discover valid IDs.' Similar guidance for delete_note (note not found), complete_action_item (action item not found), and update_action_item.
Score history
Overall score trend
↑ 7 points across a rubric change (v1 → v2)
53/100
Scored
Grade
Overall
Spec posture
Rubric
2026-09-22
D
53
2026-07-28+
v2
2026-03-09
F
46
-
v1
list_meetingsread onlyauthsource verified70/100
Search and filter Granola meetings
list_notesread onlyauthsource verified62/100
List agent notes for a meeting
meeting_statsread onlyauthsource verified57/100
Get aggregated meeting statistics with visual dashboard
Destructive operations (delete_note) lack confirmation or dry-run patterns. delete_note accepts only a note_id with no opportunity for the agent to preview or confirm before executing. This risks irreversible data loss if the agent makes a mistake.
Write/destructive operations lack error classification and recovery guidance. No evidence that delete_note or add_note return actionable error messages like 'Note not found. Available notes: [...]' or guidance on retry-ability.
Tool annotations (readOnlyHint, destructiveHint, idempotentHint) are missing from all tool definitions. LLMs cannot determine which tools are safe to retry, which modify state, and which are read-only. This is essential for agent planning.
Parameter descriptions are minimal or missing details on format and constraints. E.g., 'since' and 'until' parameters in list_meetings lack format specification (expected ISO8601 exactly, or flexible?). Numeric parameters like 'limit' and 'offset' have no min/max bounds documented.
Pagination is partially supported (limit/offset in list_meetings, search_transcripts) but no total_count or next_cursor is documented in return schemas. Without knowing the total result set size, agents cannot reason about remaining items or optimal batch sizes.
meeting_stats tool lacks clarity on what 'visual dashboard' means and whether it returns formatted text, structured metrics, or HTML. The description does not specify the response structure.
meeting_stats
Document parameter constraints explicitly in descriptions. For list_meetings: 'since' and 'until' must be ISO8601 timestamps (e.g., '2024-01-15T10:30:00Z'). For limit: min=1, max=100, default=20. For offset: min=0, default=0. For source: one of [zoom, google_meet, teams, other].
Add a total_count field to paginated responses (list_meetings, search_transcripts). This tells the agent how many results remain and helps it decide whether to fetch the next page.
Clarify meeting_stats output. Specify whether it returns structured JSON with aggregated counts/duration/participant stats, or a pre-formatted text dashboard. Document the exact structure: {total_meetings: int, total_participants: int, total_duration_hours: float, meetings_by_source: {zoom: int, google_meet: int, ...}, participants_by_frequency: {frequent: int, occasional: int}, ...}.
For export_embeddings, document the JSONL format structure. What fields does each line contain? Example: '{"meeting_id": "abc123", "chunk_index": 0, "content": "...", "speaker": "Alice", "timestamp": "2024-01-15T10:30:00Z"}'.
Add dependency hints to discovery tools. E.g., list_meetings description: 'Call this first to discover available meetings. Then use get_meeting or get_transcript with a specific meeting_id.'