← Public packages
@charlesmatte/granola
Search and read Granola meeting notes, summaries and transcripts from any agent; manage webhooks and legal holds safely.
src/index.ts
63 lines · 4.2 KB · TypeScriptimport { GRANOLA_HOST, GRANOLA_ORIGIN, SECRET_PREFIX } from './lib/account.ts'
import { discoverAccounts, userSecrets } from './lib/secrets.ts'
import { DOCS } from './lib/urls.ts'
import { parseInput } from './lib/validate.ts'
const EXPORTS = {
'./accounts': 'Configured accounts (secrets granola-<account>) and their Kody host approval',
'./verify': 'Prove a key works and see which endpoints it can reach (read-only probes)',
'./notes/list': 'List notes (date/folder filters, local title/owner filter, auto-pagination)',
'./notes/get': 'One note: summary (Markdown), attendees, calendar event, folders, private notes, optional transcript',
'./notes/search': 'Keyword search (words, "phrases", -exclude) over titles, summaries, attendees, private notes and optionally transcripts; ranked with snippets',
'./notes/transcript': 'Full transcript in pages, as readable text and/or raw items',
'./folders/list': 'Folders with computed paths and optional tree',
'./audit/list': 'Workspace audit events (needs an Audit API key, Enterprise)',
'./webhooks/list': 'Webhook endpoints this key manages',
'./webhooks/create': 'WRITE (confirm): register an HTTPS endpoint for note events',
'./webhooks/update': 'WRITE (confirm): change URL/scopes/events/folders/enabled',
'./webhooks/delete': 'WRITE (confirm): delete an endpoint',
'./legal-holds/list': 'Legal holds (Legal hold API key, Enterprise)',
'./legal-holds/get': 'One legal hold',
'./legal-holds/create': 'WRITE (confirm): place a hold',
'./legal-holds/update': 'WRITE (confirm): rename / change workspace coverage',
'./legal-holds/release': 'WRITE (confirm): release a hold (terminal)',
'./legal-holds/custodians/list': 'People named on a hold',
'./legal-holds/custodians/add': 'WRITE (confirm): name people on a hold',
'./legal-holds/custodians/remove': 'WRITE (confirm): remove a custodian',
'./mcp/tools': 'Optional MCP lane: connection status and tools of Granola’s official MCP server',
'./mcp/connect': 'WRITE (confirm): add Granola’s MCP server to Kody (browser OAuth sign-in)',
'./mcp/query': 'Semantic search: ask Granola a question about your meetings (MCP query_granola_meetings), answer with citations',
'./mcp/call': 'Call any read-only Granola MCP tool',
}
/**
* Overview of the Granola package: how accounts and keys work, every export, and the accounts
* already configured in this Kody account (secret metadata only; no Granola request).
* Start here, then ./verify({ account }) and ./notes/list({ account }).
*
* @returns `{ api, auth, accounts, exports, docs, safety }`
*
* @example
* import granola from 'kody:@charlesmatte/granola'
*
* const overview = await granola({})
*/
export default async function granolaOverview(input: unknown) {
parseInput(input, {}, 'granola')
const accounts = discoverAccounts(await userSecrets())
return {
api: { baseUrl: GRANOLA_ORIGIN, plans: 'Personal/workspace API keys: Business or Enterprise (notes, search, folders, webhooks). Audit log: Enterprise + separate Audit API key. Legal holds: Enterprise + separate Legal hold API key. MCP lane: every plan.', rateLimit: 'burst 25 requests, then 5 requests/second (429 is retried with backoff)' },
auth: {
convention: `Kody user secret ${SECRET_PREFIX}<account> = a Granola API key (grn_…), allowed host ${GRANOLA_HOST}. Pass { account } on every call; there is no default.`,
createKey: `Granola desktop app → Settings → Connectors → API keys → Create new key (${DOCS.apiKeys})`,
},
accounts: accounts?.map((a) => ({ account: a.account, ready: a.ready, hostApprovalUrl: a.hostApprovalUrl })) ?? 'not listable here; see ./accounts',
exports: EXPORTS,
search: {
keyword: './notes/search({ account, query }) — API key; scans notes (default 100 newest, throttled to 5 req/s), exact words/phrases, snippets.',
semantic: './mcp/query({ question }) — Granola MCP; natural-language answer with citations. One-time setup at https://kody.codes/account/mcp-servers (server "granola", URL https://mcp.granola.ai/mcp, then sign in to Granola).',
},
docs: DOCS,
safety: 'Reads are free. Every write previews by default and runs only with confirm: true (dryRun: true always wins). Keys never enter the isolate: Kody injects them for the approved host only.',
}
}