analyze_meeting_patterns
Analyze across meetings to identify patterns in topics, participants, and frequency. Apply optional date range filters to focus on specific time periods.
Instructions
Analyze patterns across multiple meetings
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| pattern_type | Yes | Type of pattern to analyze (topics, participants, frequency) | |
| date_range | No | Optional date range for analysis |
Implementation Reference
- granola_mcp_server/server.py:604-640 (handler)Main handler for analyze_meeting_patterns - dispatches to sub-handlers based on pattern_type (participants, frequency, topics)
async def _analyze_meeting_patterns(self, pattern_type: str, date_range: Optional[Dict] = None) -> List[TextContent]: """Analyze patterns across meetings.""" if not self.cache_data: return [TextContent(type="text", text="No meeting data available")] meetings = list(self.cache_data.meetings.values()) # Filter by date range if provided if date_range: start_date_str = date_range.get("start_date", "1900-01-01") end_date_str = date_range.get("end_date", "2100-01-01") # Parse dates and ensure timezone-aware naive_start = datetime.fromisoformat(start_date_str) naive_end = datetime.fromisoformat(end_date_str) # Localize naive datetimes to UTC if naive_start.tzinfo is None: start_date = naive_start.replace(tzinfo=zoneinfo.ZoneInfo('UTC')) else: start_date = naive_start if naive_end.tzinfo is None: end_date = naive_end.replace(tzinfo=zoneinfo.ZoneInfo('UTC')) else: end_date = naive_end meetings = [m for m in meetings if start_date <= m.date <= end_date] if pattern_type == "participants": return await self._analyze_participant_patterns(meetings) elif pattern_type == "frequency": return await self._analyze_frequency_patterns(meetings) elif pattern_type == "topics": return await self._analyze_topic_patterns(meetings) else: return [TextContent(type="text", text=f"Unknown pattern type: {pattern_type}")] - granola_mcp_server/server.py:642-663 (handler)Sub-handler for participant pattern analysis - counts meetings per participant
async def _analyze_participant_patterns(self, meetings: List[MeetingMetadata]) -> List[TextContent]: """Analyze participant patterns.""" participant_counts = {} for meeting in meetings: for participant in meeting.participants: participant_counts[participant] = participant_counts.get(participant, 0) + 1 if not participant_counts: return [TextContent(type="text", text="No participant data found")] sorted_participants = sorted(participant_counts.items(), key=lambda x: x[1], reverse=True) output = [ f"# Participant Analysis ({len(meetings)} meetings)\n", "## Most Active Participants\n" ] for participant, count in sorted_participants[:10]: output.append(f"• **{participant}:** {count} meetings") return [TextContent(type="text", text="\n".join(output))] - granola_mcp_server/server.py:665-687 (handler)Sub-handler for frequency pattern analysis - groups meetings by month
async def _analyze_frequency_patterns(self, meetings: List[MeetingMetadata]) -> List[TextContent]: """Analyze meeting frequency patterns.""" if not meetings: return [TextContent(type="text", text="No meetings found for analysis")] # Group by month monthly_counts = {} for meeting in meetings: month_key = meeting.date.strftime("%Y-%m") monthly_counts[month_key] = monthly_counts.get(month_key, 0) + 1 output = [ f"# Meeting Frequency Analysis ({len(meetings)} meetings)\n", "## Meetings by Month\n" ] for month, count in sorted(monthly_counts.items()): output.append(f"• **{month}:** {count} meetings") avg_per_month = len(meetings) / len(monthly_counts) if monthly_counts else 0 output.append(f"\n**Average per month:** {avg_per_month:.1f}") return [TextContent(type="text", text="\n".join(output))] - granola_mcp_server/server.py:689-716 (handler)Sub-handler for topic pattern analysis - keyword extraction from meeting titles
async def _analyze_topic_patterns(self, meetings: List[MeetingMetadata]) -> List[TextContent]: """Analyze topic patterns from meeting titles.""" if not meetings: return [TextContent(type="text", text="No meetings found for analysis")] # Simple keyword extraction from titles word_counts = {} for meeting in meetings: words = meeting.title.lower().split() for word in words: # Filter out common words if len(word) > 3 and word not in ['meeting', 'call', 'sync', 'with']: word_counts[word] = word_counts.get(word, 0) + 1 if not word_counts: return [TextContent(type="text", text="No significant topics found in meeting titles")] sorted_topics = sorted(word_counts.items(), key=lambda x: x[1], reverse=True) output = [ f"# Topic Analysis ({len(meetings)} meetings)\n", "## Most Common Topics (from titles)\n" ] for topic, count in sorted_topics[:15]: output.append(f"• **{topic}:** {count} mentions") return [TextContent(type="text", text="\n".join(output))] - granola_mcp_server/server.py:169-191 (registration)Tool registration with name and inputSchema (pattern_type enum, optional date_range)
Tool( name="analyze_meeting_patterns", description="Analyze patterns across multiple meetings", inputSchema={ "type": "object", "properties": { "pattern_type": { "type": "string", "description": "Type of pattern to analyze (topics, participants, frequency)", "enum": ["topics", "participants", "frequency"] }, "date_range": { "type": "object", "properties": { "start_date": {"type": "string", "format": "date"}, "end_date": {"type": "string", "format": "date"} }, "description": "Optional date range for analysis" } }, "required": ["pattern_type"] } ) - granola_mcp_server/server.py:210-214 (helper)Call-tool dispatch routing 'analyze_meeting_patterns' to the handler method
elif name == "analyze_meeting_patterns": return await self._analyze_meeting_patterns( pattern_type=arguments["pattern_type"], date_range=arguments.get("date_range") )