# Granola — MCP transcript ingest reference

Companion to [`conversation-archive-mcp`](../../skills/conversation-archive-mcp/SKILL.md). Names the install command, the expected MCP tool surface, the returned shape, and how to map that shape into `memory-ingest` parameters for the conversation-archive writer.

## Install (operator)

Granola ships a remote streamable-HTTP MCP server. Install once per Claude Code host:

```bash
claude mcp add --transport http granola <granola-mcp-url>
```

Granola's docs live at https://docs.granola.ai/help-center/sharing/integrations/mcp — read the linked page for the current MCP URL and any auth pre-step. First invocation opens an OAuth flow in the operator's browser; the operator authorises which notebooks / meetings Granola exposes to this Claude Code session. Tokens live in the host, not in maxy.

**Operator-facing verification.** After install, run `/mcp` in admin chat. The expected entry is `granola — connected` with a tool count > 0. If the entry is missing or shows an error, re-run the install one-liner before invoking the ingest skill.

## Expected tool surface

The skill discovers tools by name pattern at run time. Expected tools (names may drift across vendor doc revisions — run `/mcp` to confirm the live set):

- **List meetings / notes.** Pattern: `mcp__granola__list_notes`, `mcp__granola__search_notes`, or similar. Accepts a date window and/or text filter; returns an array of note objects with at minimum `id`, `title`, `createdAt`, `attendees`.
- **Fetch a single note's transcript.** Pattern: `mcp__granola__get_note`, `mcp__granola__read_note`, or similar. Accepts a note ID; returns the full note object including the transcript turns.

If the discovered tools diverge from these patterns, prefer matching by description over matching by name (the host exposes each tool's description). If two tools could plausibly serve "list meetings", surface the disambiguation to the operator rather than guessing.

## Returned shape (typical)

A Granola note object carries the following fields relevant to ingest:

| Granola field | Maps to memory-ingest |
|---|---|
| `id` | `granolaNoteId` (cross-reference property on `:ConversationArchive`) |
| `title` | `archiveTitle` (prefixed with `"granola · "` and suffixed with the ISO date) |
| `createdAt` / `meetingStartTime` | `lastIngestedMessageAt` fallback when transcript turns lack per-turn timestamps |
| `attendees[]` — each with `displayName`, `email` | Per-attendee resolution against `:AdminUser` / `:Person` (Step 4 of the skill) |
| `transcript[]` — each with `speaker`, `startTime`, `text` | Rendered into turn-attributed `[<ISO>] <speaker>: <text>` lines for the typed-section JSON |
| `calendarEventId` / `googleCalendarEventId` / `outlookEventId` | `calendarEventId` cross-reference property on `:ConversationArchive` (whichever provider field is populated; normalise to one property) |
| `summary` / `aiSummary` | NOT used for `:Section` summaries — the in-turn classifier produces section summaries from the transcript itself; Granola's AI summary is provider-authored and would bias the chunking |

## Edge cases

- **Note exists but transcript is empty** (the operator joined the meeting but Granola did not record): FAIL for that meeting with `phase=fetch reason="granola note <id> has empty transcript"`. The empty parent is not written.
- **Attendee with no email** (operator added a free-text name to a Granola attendee list): falls through to the per-attendee prompt loop. The operator picks an existing `:Person` or blocks.
- **Continuation lines** (Granola occasionally emits multi-line transcript entries with embedded newlines): collapse internal newlines to spaces; one transcript entry = one rendered line.
- **Retention** — Granola retains notes per the operator's plan. If a note returned by `list_notes` cannot be fetched via `get_note` (404), surface the provider error verbatim and continue with the next note.

## What this reference is not

- Not a Granola API reference. The MCP surface is the contract — this doc only covers the fields the ingest skill consumes. If Granola adds new tools or fields the skill should use, edit this doc and the skill in the same commit.
- Not a token manager. The OAuth dance is entirely in Claude Code's MCP host; maxy never sees a Granola token.
