import { callMcpTool, DEFAULT_MCP_SERVER, extractCitations, MCP_SERVER_PATTERN, mcpText } from '../lib/mcp.ts' import { parseInput } from '../lib/validate.ts' /** * Ask Granola a natural-language question about your meetings ("What did we decide about pricing * with Acme?", "Action items from last week?") through Granola's official MCP tool * `query_granola_meetings`. Granola answers from your notes with inline citations like `[[0]](url)` * pointing to the source meetings — keep them when relaying the answer. Semantic, so it finds * meetings that keyword search (./notes/search) misses; read-only. Needs the one-time MCP * connection (./mcp/connect, browser sign-in); works on every Granola plan (Basic: last 30 days). * * @param input.question - The question, in plain language. Required. * @param input.documentIds - Optional MCP meeting ids (from the MCP `list_meetings` tool) to limit context. * @param input.server - Kody MCP server name (default `granola`). * @param input.includeRaw - Also return the raw MCP result (default false). * @returns `{ server, question, answer, citations: [{ ref, url }], isError, raw? }` * * @example * import askGranola from 'kody:@charlesmatte/granola/mcp/query' * * const { answer, citations } = await askGranola({ question: 'What action items came out of meetings this week?' }) */ export default async function queryGranolaMeetings(input: unknown) { const p = parseInput<{ question: string; documentIds?: Array; server?: string; includeRaw?: boolean }>( input, { question: { type: 'string', required: true, maxLength: 4000 }, documentIds: { type: 'stringArray', maxItems: 50 }, server: { type: 'string', pattern: MCP_SERVER_PATTERN }, includeRaw: { type: 'boolean' }, }, 'mcp/query', ) const server = p.server ?? DEFAULT_MCP_SERVER const args: Record = { query: p.question, ...(p.documentIds?.length ? { document_ids: p.documentIds } : {}) } const raw = await callMcpTool(server, 'query_granola_meetings', args) const { text, isError } = mcpText(raw) return { server, question: p.question, answer: text, citations: extractCitations(text), isError, ...(isError ? { hint: 'Granola returned an error. If it mentions arguments, inspect the tool schema with Kody search({ entity: "mcp-server:granola" }) and use ./mcp/call.' } : {}), ...(p.includeRaw ? { raw } : {}), } }