granola-mcp
granola-mcp
MCP server for semantic search across Granola meeting notes. Extracts insights, themes (pain-points, feature-requests, decisions, etc.), and key quotes with speaker attribution. Uses LanceDB for fast local vector search.
Based on reverse engineering research by Joseph Thacker and getprobo.
Features
Export: Extract all your Granola meetings with transcripts
Semantic Search: Vector-indexed search across meetings with pre-extracted insights
Speaker Attribution: Distinguishes between host (
me) and participantsTheme Extraction: Auto-categorizes content into themes (pain-points, feature-requests, etc.)
MCP Server: Exposes search to Claude Code, Claude Desktop, and other AI tools
Prerequisites
Node.js 18+
Granola desktop app installed and logged in
OpenAI API key (for embeddings and insight extraction)
Installation
npm install
npm run buildQuick Start
# One command to sync everything (export + index)
OPENAI_API_KEY=sk-... node dist/index.js sync
# Or with a custom data directory
OPENAI_API_KEY=sk-... node dist/index.js sync ./my-data
# Test search
OPENAI_API_KEY=sk-... node dist/index.js search "user pain points"CLI Commands
Sync (Recommended)
The easiest way to keep your data up to date - exports from Granola and rebuilds the index in one step:
OPENAI_API_KEY=sk-... node dist/index.js sync
# With options
OPENAI_API_KEY=sk-... node dist/index.js sync ./my-data
OPENAI_API_KEY=sk-... node dist/index.js sync --skip-extraction # Faster, reuses existing insightsExport from Granola
Export only (without indexing):
node dist/index.js export ./output
node dist/index.js export ./output --format markdown
node dist/index.js export ./output --format jsonBuild Search Index
# Full indexing with insight extraction (~$0.02/document)
OPENAI_API_KEY=sk-... node dist/index.js index ./export
# Skip extraction (use existing insights, just rebuild embeddings)
OPENAI_API_KEY=sk-... node dist/index.js index ./export --skip-extractionSearch from CLI
OPENAI_API_KEY=sk-... node dist/index.js search "pricing concerns"
OPENAI_API_KEY=sk-... node dist/index.js search "feature requests" --folder "User interviews"Export for ChatGPT
OPENAI_API_KEY=sk-... node dist/index.js export-combined ./chatgpt.md
OPENAI_API_KEY=sk-... node dist/index.js export-combined ./chatgpt.md --query "user feedback"Other Commands
node dist/index.js list # List documents
node dist/index.js workspaces # List workspaces
node dist/index.js folders # List folders
node dist/index.js transcript <id> # Get specific transcriptMCP Server Setup
Claude Code
Add to .mcp.json in your project:
{
"mcpServers": {
"granola": {
"type": "stdio",
"command": "node",
"args": ["/path/to/granola-mcp/dist/mcp/server.js"],
"env": {
"GRANOLA_DATA_DIR": "/path/to/granola-mcp/export",
"OPENAI_API_KEY": "sk-..."
}
}
}
}Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"granola": {
"command": "node",
"args": ["/path/to/granola-mcp/dist/mcp/server.js"],
"env": {
"GRANOLA_DATA_DIR": "/path/to/granola-mcp/export",
"OPENAI_API_KEY": "sk-..."
}
}
}
}MCP Tools
Tool | Description |
| Semantic search across meetings, returns summaries + quotes |
| Find documents by theme (pain-points, feature-requests, etc.) |
| List all folders with document counts |
| List documents with brief summaries |
| Get full document details (all themes + quotes) |
| Get raw transcript (use sparingly) |
| List available themes with definitions |
Speaker Attribution
The system distinguishes between speakers:
speaker: "me"- The meeting host (you)speaker: "participant"- Other people in the meeting
This helps AI understand what's your own idea vs external feedback.
Pre-defined Themes
pain-points: User frustrations, problems, complaints
feature-requests: Desired features, wishlist items
positive-feedback: What users liked, praised
pricing: Cost concerns, value perception
competition: Competitor mentions, alternatives
workflow: How users currently do things
decisions: Key decisions made, action items
questions: Open questions needing clarification
Output Structure
export/
├── vectors.lance/ # LanceDB vector index
├── Meeting_Title_1/
│ ├── document.json # Raw document data
│ ├── notes.md # Converted notes
│ ├── transcript.json # Raw transcript with speaker info
│ ├── transcript.md # Formatted transcript
│ └── transcript.txt # Plain text transcript
└── Meeting_Title_2/
└── ...Keeping Data Updated
The system doesn't auto-sync with Granola. Run sync manually after new meetings, or set up a cron job:
Manual Update
OPENAI_API_KEY=sk-... node dist/index.js syncAutomated Updates (Cron)
Add to your crontab (crontab -e):
# Sync every night at 2am
0 2 * * * cd /path/to/granola-mcp && OPENAI_API_KEY=sk-... /usr/local/bin/node dist/index.js sync >> /tmp/granola-sync.log 2>&1
# Or every 6 hours
0 */6 * * * cd /path/to/granola-mcp && OPENAI_API_KEY=sk-... /usr/local/bin/node dist/index.js sync >> /tmp/granola-sync.log 2>&1macOS LaunchAgent
Create ~/Library/LaunchAgents/com.granola-mcp.sync.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.granola-mcp.sync</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/node</string>
<string>/path/to/granola-mcp/dist/index.js</string>
<string>sync</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>OPENAI_API_KEY</key>
<string>sk-...</string>
</dict>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>StandardOutPath</key>
<string>/tmp/granola-sync.log</string>
<key>StandardErrorPath</key>
<string>/tmp/granola-sync.log</string>
</dict>
</plist>Load it with: launchctl load ~/Library/LaunchAgents/com.granola-mcp.sync.plist
How It Works
Export: Reads credentials from
~/Library/Application Support/Granola/supabase.jsonand fetches all documents via Granola's APIIndex:
Extracts themes and key quotes using GPT-4o-mini
Generates embeddings using text-embedding-3-small
Stores in LanceDB for fast vector search
Search:
Embeds your query
Finds semantically similar documents
Returns summaries + relevant quotes (not raw transcripts)
Cost Estimates
Documents | Insight Extraction | Embeddings | Total |
25 | ~$0.50 | ~$0.01 | ~$0.51 |
100 | ~$2.00 | ~$0.02 | ~$2.02 |
500 | ~$10.00 | ~$0.10 | ~$10.10 |
Search queries are free (vector similarity, no LLM calls).
License
MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/mishkinf/granola-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server