search_meetings
Find meetings by searching titles, content, or participants to quickly locate specific discussions and information within your meeting records.
Instructions
Search meetings by title, content, or participants
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search query for meetings | |
| limit | No | Maximum number of results |
Implementation Reference
- src/server.ts:136-192 (handler)Main handler function for search_meetings tool that searches through cached meetings by title and content, scoring results, and returning formatted output with meeting metadataexecute: async ({ query, limit }) => { await ensureDataLoaded(); if (documentsCache.size === 0) { return 'No meeting data available'; } const queryLower = safeString(query).toLowerCase(); const results: Array<{ score: number; doc: GranolaDocument }> = []; for (const doc of documentsCache.values()) { let score = 0; // Search in title (with null check) const title = safeString(doc.title).toLowerCase(); if (title.includes(queryLower)) { score += 2; } // Search in content const content = safeString(extractContent(doc)).toLowerCase(); if (content.includes(queryLower)) { score += 1; } if (score > 0) { results.push({ score, doc }); } } results.sort((a, b) => b.score - a.score); const topResults = results.slice(0, limit); if (topResults.length === 0) { return `No meetings found matching '${query}'`; } const output: string[] = [`Found ${topResults.length} meeting(s) matching '${query}':\n`]; for (const { doc } of topResults) { output.push(`• **${getTitle(doc)}** (${doc.id})`); output.push(` Date: ${formatDate(doc.created_at)}`); if (doc.workspace_name) { output.push(` Workspace: ${doc.workspace_name}`); } if (doc.folders && doc.folders.length > 0) { const folderNames = doc.folders.map(f => f.name).join(', '); output.push(` Folders: ${folderNames}`); } output.push(''); } return output.join('\n'); }
- src/server.ts:128-193 (registration)Tool registration using FastMCP server.addTool() with name, description, parameter schema (Zod), and execute handler for search_meetings// Tool: search_meetings server.addTool({ name: 'search_meetings', description: 'Search meetings by title, content, or participants', parameters: z.object({ query: z.string().describe('Search query for meetings'), limit: z.number().optional().default(10).describe('Maximum number of results') }), execute: async ({ query, limit }) => { await ensureDataLoaded(); if (documentsCache.size === 0) { return 'No meeting data available'; } const queryLower = safeString(query).toLowerCase(); const results: Array<{ score: number; doc: GranolaDocument }> = []; for (const doc of documentsCache.values()) { let score = 0; // Search in title (with null check) const title = safeString(doc.title).toLowerCase(); if (title.includes(queryLower)) { score += 2; } // Search in content const content = safeString(extractContent(doc)).toLowerCase(); if (content.includes(queryLower)) { score += 1; } if (score > 0) { results.push({ score, doc }); } } results.sort((a, b) => b.score - a.score); const topResults = results.slice(0, limit); if (topResults.length === 0) { return `No meetings found matching '${query}'`; } const output: string[] = [`Found ${topResults.length} meeting(s) matching '${query}':\n`]; for (const { doc } of topResults) { output.push(`• **${getTitle(doc)}** (${doc.id})`); output.push(` Date: ${formatDate(doc.created_at)}`); if (doc.workspace_name) { output.push(` Workspace: ${doc.workspace_name}`); } if (doc.folders && doc.folders.length > 0) { const folderNames = doc.folders.map(f => f.name).join(', '); output.push(` Folders: ${folderNames}`); } output.push(''); } return output.join('\n'); } });
- src/server.ts:132-135 (schema)Zod schema defining input parameters for search_meetings: query (required string) and limit (optional number with default 10)parameters: z.object({ query: z.string().describe('Search query for meetings'), limit: z.number().optional().default(10).describe('Maximum number of results') }),
- src/server.ts:30-35 (helper)Helper function ensureDataLoaded() that checks if data cache is empty or stale, and triggers data loading if neededasync function ensureDataLoaded() { const now = Date.now(); if (documentsCache.size === 0 || (now - lastFetchTime) > CACHE_TTL) { await loadData(); } }
- src/server.ts:99-110 (helper)Helper function extractContent() that extracts text content from GranolaDocument's ProseMirror structure for searchingfunction extractContent(doc: GranolaDocument): string { if (!doc.last_viewed_panel?.content) { return ''; } const content = doc.last_viewed_panel.content; if (content.type === 'doc') { return extractTextFromProseMirror(content); } return ''; }