get_meeting_content
Retrieve complete meeting notes and transcripts in Markdown format using a meeting ID. Access organized meeting content for review and reference.
Instructions
Get the full notes/content for a meeting in Markdown format
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| meeting_id | Yes | Meeting ID to retrieve content for |
Implementation Reference
- src/server.ts:236-257 (handler)Main handler function for get_meeting_content tool. Executes the tool logic: ensures data is loaded, retrieves the document by meeting_id, extracts and converts content to Markdown, and returns the formatted result with title.
server.addTool({ name: 'get_meeting_content', description: 'Get the full notes/content for a meeting in Markdown format', parameters: z.object({ meeting_id: z.string().describe('Meeting ID to retrieve content for') }), execute: async ({ meeting_id }) => { await ensureDataLoaded(); const doc = documentsCache.get(meeting_id); if (!doc) { return `Meeting '${meeting_id}' not found`; } const content = extractAndConvertContent(doc); if (!content) { return `No content available for meeting '${getTitle(doc)}'`; } return `# ${getTitle(doc)}\n\n${content}`; } }); - src/server.ts:239-241 (schema)Input parameter schema for get_meeting_content tool. Defines the required 'meeting_id' string parameter using zod validation.
parameters: z.object({ meeting_id: z.string().describe('Meeting ID to retrieve content for') }), - src/server.ts:115-126 (helper)Helper function extractAndConvertContent that extracts content from a GranolaDocument's last_viewed_panel and converts it from ProseMirror format to Markdown.
function extractAndConvertContent(doc: GranolaDocument): string { if (!doc.last_viewed_panel?.content) { return ''; } const content = doc.last_viewed_panel.content; if (content.type === 'doc') { return convertProseMirrorToMarkdown(content); } return ''; } - src/server.ts:92-94 (helper)Helper function getTitle that retrieves the title from a GranolaDocument, returning 'Untitled Meeting' if no title exists.
function getTitle(doc: GranolaDocument): string { return doc.title || 'Untitled Meeting'; } - src/prosemirrorConverter.ts:8-119 (helper)Core conversion function convertProseMirrorToMarkdown that transforms ProseMirror JSON content into formatted Markdown, supporting headings, paragraphs, lists, code blocks, blockquotes, text formatting, and links.
export function convertProseMirrorToMarkdown(content: ProseMirrorContent | null | undefined): string { if (!content || !content.content) { return ''; } const markdownLines: string[] = []; function processNode(node: ProseMirrorNode): string { const nodeType = node.type; const contentItems = node.content || []; const text = node.text || ''; if (nodeType === 'heading') { const level = node.attrs?.level || 1; const headingText = contentItems.map(child => processNode(child)).join(''); return `${'#'.repeat(level)} ${headingText}\n\n`; } if (nodeType === 'paragraph') { const paraText = contentItems.map(child => processNode(child)).join(''); if (paraText.trim()) { return `${paraText}\n\n`; } return '\n'; } if (nodeType === 'bulletList') { const items: string[] = []; for (const item of contentItems) { if (item.type === 'listItem') { const itemContent = (item.content || []) .map(child => processNode(child)) .join(''); items.push(`- ${itemContent.trim()}`); } } if (items.length > 0) { return items.join('\n') + '\n\n'; } return ''; } if (nodeType === 'orderedList') { const items: string[] = []; const start = node.attrs?.start || 1; contentItems.forEach((item, idx) => { if (item.type === 'listItem') { const itemContent = (item.content || []) .map(child => processNode(child)) .join(''); items.push(`${start + idx}. ${itemContent.trim()}`); } }); if (items.length > 0) { return items.join('\n') + '\n\n'; } return ''; } if (nodeType === 'codeBlock') { const codeText = contentItems.map(child => processNode(child)).join(''); const language = node.attrs?.language || ''; return `\`\`\`${language}\n${codeText}\n\`\`\`\n\n`; } if (nodeType === 'blockquote') { const quoteText = contentItems.map(child => processNode(child)).join(''); const quotedLines = quoteText.trim().split('\n').map(line => `> ${line}`); return quotedLines.join('\n') + '\n\n'; } if (nodeType === 'horizontalRule') { return '---\n\n'; } if (nodeType === 'text') { let formattedText = text; const marks = node.marks || []; for (const mark of marks) { const markType = mark.type; if (markType === 'bold' || markType === 'strong') { formattedText = `**${formattedText}**`; } else if (markType === 'italic' || markType === 'em') { formattedText = `*${formattedText}*`; } else if (markType === 'code') { formattedText = `\`${formattedText}\``; } else if (markType === 'link') { const href = mark.attrs?.href || ''; formattedText = `[${formattedText}](${href})`; } else if (markType === 'strike' || markType === 'strikethrough') { formattedText = `~~${formattedText}~~`; } } return formattedText; } // Default: process child content if available return contentItems.map(child => processNode(child)).join(''); } // Process top-level content for (const node of content.content) { const result = processNode(node); if (result) { markdownLines.push(result); } } return markdownLines.join('').trim(); }