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
| 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 metadata
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: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 needed
async 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 searching
function 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 ''; }