Integrate Granola meeting notes into your local development workflow.
Use when setting up development workflows, accessing notes programmatically,
or syncing meeting outcomes with project tools.
Trigger with phrases like "granola dev workflow", "granola development",
"granola local setup", "granola developer", "granola coding workflow".
Access Granola meeting data programmatically using three methods: the local cache file (zero-auth, offline), the MCP server (AI agent integration), or the Enterprise API (workspace-wide access). Build developer workflows that turn meeting outcomes into code tasks, documentation, and project artifacts.
Prerequisites
Granola installed with meetings captured
Node.js 18+ or Python 3.10+ for scripts
For MCP: Claude Code, Cursor, or another MCP-compatible client
For Enterprise API: Business/Enterprise plan + API key
Instructions
Step 1 — Read the Local Cache (Zero Auth)
Granola stores meeting data in a local JSON cache file:
# macOS cache location
CACHE_FILE="$HOME/Library/Application Support/Granola/cache-v3.json"
# Check if cache exists and get size
ls -lh "$CACHE_FILE"
The cache has a double-JSON structure (JSON string inside JSON):
#!/usr/bin/env python3
"""Extract meetings from Granola local cache."""
import json
from pathlib import Path
CACHE_PATH = Path.home() / "Library/Application Support/Granola/cache-v3.json"
def load_granola_cache():
raw = json.loads(CACHE_PATH.read_text())
# Cache contains a JSON string that needs secondary parsing
state = json.loads(raw) if isinstance(raw, str) else raw
data = state.get("state", state)
return {
"documents": data.get("documents", {}),
"transcripts": data.get("transcripts", {}),
"meetings_metadata": data.get("meetingsMetadata", {}),
}
cache = load_granola_cache()
docs = cache["documents"]
print(f"Found {len(docs)} meetings in local cache")
# List recent meetings
for doc_id, doc in sorted(docs.items(),
key=lambda x: x[1].get("updated_at", ""),
reverse=True)[:10]:
print(f" {doc.get('title', 'Untitled')} — {doc.get('updated_at', 'N/A')}")
Step 2 — Set Up Granola MCP Server
Granola's official MCP integration connects meeting context to AI tools:
mishkinf/granola-mcp — semantic search with LanceDB vector embeddings
proofgeist/granola-mcp-server — lightweight local cache reader
Step 3 — Extract Action Items to Dev Tools
#!/usr/bin/env python3
"""Extract action items from Granola notes and create GitHub issues."""
import json, re, subprocess
from pathlib import Path
def extract_action_items(note_content: str) -> list[dict]:
"""Parse action items from enhanced Granola notes."""
items = []
# Matches: - [ ] @person: task description
pattern = r'- \[ \] @?(\w+):?\s+(.+)'
for match in re.finditer(pattern, note_content):
items.append({
"assignee": match.group(1),
"task": match.group(2).strip(),
})
return items
def create_github_issue(repo: str, title: str, body: str, assignee: str):
"""Create a GitHub issue using gh CLI."""
cmd = [
"gh", "issue", "create",
"--repo", repo,
"--title", title,
"--body", body,
"--assignee", assignee,
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print(f" Created: {result.stdout.strip()}")
else:
print(f" Error: {result.stderr.strip()}")
# Usage with cache data
cache = load_granola_cache() # from Step 1
for doc_id, doc in cache["documents"].items():
content = doc.get("last_viewed_panel", {})
# ProseMirror content needs text extraction
text = json.dumps(content) # simplified — parse nodes for production
actions = extract_action_items(text)
for action in actions:
print(f"[{action['assignee']}] {action['task']}")
Step 4 — Sync Meeting Outcomes to Project Docs
#!/bin/bash
set -euo pipefail
# Sync latest Granola meeting notes to project documentation
NOTES_DIR="$HOME/dev/meeting-notes"
mkdir -p "$NOTES_DIR"
# Extract recent meeting titles and dates using Python
python3 -c "
import json
from pathlib import Path
cache_path = Path.home() / 'Library/Application Support/Granola/cache-v3.json'
if cache_path.exists():
raw = json.loads(cache_path.read_text())
state = json.loads(raw) if isinstance(raw, str) else raw
data = state.get('state', state)
docs = data.get('documents', {})
for doc_id, doc in sorted(docs.items(),
key=lambda x: x[1].get('updated_at', ''),
reverse=True)[:5]:
title = doc.get('title', 'Untitled').replace(' ', '-').lower()
date = doc.get('created_at', 'unknown')[:10]
print(f'{date}_{title}')
"
Step 5 — Git Integration Pattern
Reference Granola meetings in commits and PRs:
# Reference meeting in commit message
git commit -m "feat: implement user onboarding flow
Per meeting 2026-03-22 'Sprint Planning Q1':
- Agreed on 3-step wizard approach
- Sarah approved the design mockups
- Due by April 15
Action items from Granola note: [link]"
Output
Local cache accessible for offline meeting data reads
MCP server connected for AI-assisted meeting context
Action item extraction pipeline ready
Meeting-to-dev-tools sync established
Error Handling
Error
Cause
Fix
Cache file not found
Granola not installed or never launched
Install Granola and capture at least one meeting
JSON parse error
Double-JSON structure not handled
Parse the outer string first, then parse the inner object
MCP server not connecting
Wrong config path
Verify claude_desktop_config.json location for your OS