MCP server for Granola.ai meeting intelligence - search, retrieve, and filter meetings via Claude Desktop and other MCP clients
The Granola MCP server defines 7 READ_ONLY tools with moderate quality. All tools have names that follow verb_noun convention (search_, get_, list_, filter_) and descriptions between 30-80 characters. Input schemas are present with Zod validation and typed parameters. However, several critical gaps reduce the score: (1) Output schemas are not documented, tools return plain strings instead of structured objects, forcing LLMs to parse unstructured text; (2) Parameter descriptions are minimal, lacking context about format, constraints, or when to use which tool; (3) Error handling is basic, failures return plain text strings without recovery guidance; (4) No pagination support despite list_ tools that could return unbounded results; (5) Tool composition issues, filter_by_workspace and filter_by_folder duplicate search_meetings functionality without clear distinction; (6) Missing dependency hints and parameter relationships (e.g., why choose filter_by_workspace vs search_meetings?). The codebase shows competent FastMCP setup and API integration, but the tool interface is optimized for human readability rather than LLM reasoning.
Filter meetings by folder
Filter meetings by workspace
Get the full notes/content for a meeting in Markdown format
Get detailed information about a specific meeting
List all document folders
List all available workspaces
Search meetings by title, content, or participants
No output schemas documented for any tool. All tools return unstructured plain-text strings instead of structured JSON objects. LLMs cannot extract fields programmatically, cannot validate results, and cannot chain results to downstream tools. This violates the pattern:response-shaper and forces token-wasteful text parsing.
Semantic overlap between search_meetings, filter_by_workspace, and filter_by_folder. An LLM given a workspace name cannot tell whether to call search_meetings(query='workspace_name'), filter_by_workspace(workspace_id=<id>), or some other approach. The tools lack dependency hints explaining when to use each. This violates pattern:tool-composition and forces LLM reasoning overhead.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | C | 63 | 2026-07-28+ | v2 |
| 2026-03-09 | F | 0 | - | v1 |
Error responses are generic plain-text strings with no recovery guidance. E.g., 'Meeting not found' does not suggest calling search_meetings() with a partial name, or list any available meetings. Violates pattern:recovery-guide, LLMs receive no actionable next steps.
No pagination support. list_workspaces and list_folders lack limit and offset/cursor parameters. search_meetings has a limit parameter (default 10) but no offset or next_cursor for continuation. Large result sets could blow context or truncate silently. Violates pattern:paginated-result.
Parameter descriptions lack format constraints and usage guidance. E.g., meeting_id parameter description is 'Meeting ID to retrieve details for', no mention of whether it's a UUID, opaque token, or human-readable name. workspace_id and folder_id similarly underdescribed. Forces LLMs to guess valid formats, increasing errors. Violates pattern:constrained-input and review:param-validation-rules.
Tool descriptions are at minimum length (29-75 chars) with minimal context. E.g., 'List all available workspaces' lacks explanation of what a workspace is, when to call it, or what data is returned. These descriptions save tokens but lose LLM-optimization opportunities.
Naming convention inconsistency. filter_by_workspace and filter_by_folder use 'filter_by_' prefix instead of verb_noun (search_, get_, list_). This non-standard naming increases cognitive load on LLMs and risks tool selection errors. Baseline production tools use verb_noun 90% of the time.