Skip to content
← Public packages

@charlesmatte/granola

Search and read Granola meeting notes, summaries and transcripts from any agent; manage webhooks and legal holds safely.

src/mcp/connect.ts

46 lines · 2.5 KB · TypeScript
Raw
import { kody } from 'kody:runtime'
import { echo, mutationMode } from '../lib/guard.ts'
import { DEFAULT_MCP_SERVER, findMcpServer, GRANOLA_MCP_URL, MCP_SERVER_PATTERN } from '../lib/mcp.ts'
import { mutationSpec, parseInput } from '../lib/validate.ts'

/**
 * Connect Granola's official MCP server (https://mcp.granola.ai/mcp) to this Kody account so the
 * ./mcp/* exports can use it. Granola MCP uses browser OAuth: the result carries an `authUrl` the
 * Kody account owner must open to sign in to Granola (no API key; works on every Granola plan).
 * Same as adding the server by hand at https://kody.codes/account/mcp-servers (name `granola`,
 * URL https://mcp.granola.ai/mcp). Changes Kody account settings, so it previews unless `confirm: true`. Does nothing if a server
 * with that name already exists.
 *
 * @param input.server - Kody MCP server name to create (default `granola`).
 * @param input.dryRun - Preview only. @param input.confirm - `true` to add the server.
 * @returns Preview, `{ alreadyExists: true, state }`, or `{ executed: true, state, authUrl, nextStep }`
 *
 * @example
 * import connect from 'kody:@charlesmatte/granola/mcp/connect'
 *
 * const r = await connect({ confirm: true }) // then open r.authUrl in a browser
 */
export default async function connectGranolaMcp(input: unknown) {
	const p = parseInput<{ server?: string; dryRun?: boolean; confirm?: boolean }>(
		input,
		{ server: { type: 'string', pattern: MCP_SERVER_PATTERN }, ...mutationSpec },
		'mcp/connect',
	)
	const server = p.server ?? DEFAULT_MCP_SERVER
	const existing = await findMcpServer(server)
	if (existing) return { server, alreadyExists: true, url: existing.url, state: existing.state ?? existing.status, authUrl: existing.authUrl ?? null }
	const mode = mutationMode(p)
	const acct = { account: '(kody mcp)', secretName: '(oauth, stored by Kody)' }
	if (!mode.execute) {
		return {
			dryRun: true,
			executed: false,
			echo: echo(acct, 'mcp/connect', `Kody MCP server "${server}" → ${GRANOLA_MCP_URL}`, mode),
			reason: mode.reason,
			request: { capability: 'mcpServerAdd', name: server, url: GRANOLA_MCP_URL },
			next: 'Nothing was changed. Re-run with confirm: true to add the server, then open the returned authUrl to sign in to Granola.',
		}
	}
	const r = (await kody.mcpServerAdd({ name: server, url: GRANOLA_MCP_URL })) as Record<string, unknown>
	return { dryRun: false, executed: true, server, state: r.state, toolCount: r.toolCount, authUrl: r.authUrl ?? null, error: r.error ?? null, nextStep: r.nextStep }
}