ParrotNotes

Search, summarize and save insights on your ParrotNotes in-person meeting notes. Hosted remote server with OAuth sign-in, no API key.

Hosted MCP Server

npx add-mcp 'https://mcp.parrotnotes.app/mcp'

Installs into Claude Code, Codex, Cursor and more

Documentation

What it does

ParrotNotes records in-person conversations on your phone or Mac and turns them into transcripts, summaries and action items. This server exposes that library to an AI assistant so you can ask questions of your own notes without opening the app.

Typical requests:

  • "Show me my five most recent recordings."
  • "Find my notes from the Henderson site visit and summarise the key points."
  • "What did this client commit to? Save that back as an action list."
  • "List everything tagged project-alpha."

Before you start

  • A ParrotNotes account. The free tier works.
  • At least one saved note or recording, otherwise every tool returns an empty result.
  • Notes are created in the ParrotNotes apps for iOS, Android and macOS. This server reads that existing library and can save an insight back to a note. It cannot create recordings.

No API key, client ID or manual configuration is required. Clients register themselves through Dynamic Client Registration and you sign in through the browser.

Connecting

Claude

  1. Open Settings > Connectors > Add custom connector.
  2. Paste the endpoint:
https://mcp.parrotnotes.app/mcp
  1. You are redirected to https://parrotnotes.app/auth/authorize to sign in and grant consent.
  2. After approval the tools are available in any conversation.

Claude Code

claude mcp add --transport http parrotnotes https://mcp.parrotnotes.app/mcp

Then run /mcp inside Claude Code, select parrotnotes and complete the sign-in in your browser.

ChatGPT

Add the same endpoint as a custom connector. The OAuth flow is identical.

MCP Inspector

npx @modelcontextprotocol/inspector

Set transport to Streamable HTTP, URL to https://mcp.parrotnotes.app/mcp, and connect. The inspector completes the OAuth flow in the browser.

Verify discovery

An unauthenticated request returns 401 with a pointer to the protected resource metadata, which is how clients discover the authorization server:

curl -i -X POST https://mcp.parrotnotes.app/mcp \
  -H "Content-Type: application/json" \
  -d '{}'
HTTP/2 401
www-authenticate: Bearer realm="parrot-notes", resource_metadata="https://mcp.parrotnotes.app/.well-known/oauth-protected-resource", scope="openid email profile"
curl https://mcp.parrotnotes.app/.well-known/oauth-protected-resource

The response lists the resource (https://mcp.parrotnotes.app/mcp), its authorization_servers, scopes_supported and bearer_methods_supported (header).

Tools

ToolWhat it doesAccess
get_recent_notesLists recent notes and recordings, with pagination and a type filterRead
search_notesFull-text search across your notes by keywordRead
get_noteReturns a single note by ID with its full transcript and contentRead
get_note_insightsReturns AI insights saved against a note: summaries, action items, sentimentRead
get_user_labelsLists every label you have createdRead
get_note_labelsReturns the labels assigned to a specific noteRead
get_notes_by_labelReturns all notes carrying a given labelRead
get_subscription_statusReturns your current subscription statusRead
save_note_insightSaves an AI-generated insight against a noteWrite

Eight of the nine tools are read-only and carry readOnlyHint: true. save_note_insight is the only tool that changes data: it upserts an insight on a note you own and can overwrite an existing insight of the same type and target language, so it carries destructiveHint: true. Nothing is ever deleted. Every tool sets openWorldHint: false and idempotentHint: true.

Every tool declares an output schema and returns validated structuredContent, with the same JSON mirrored as text in content for clients that only read text.

Identifiers

Notes carry two IDs. Use the right one for each tool:

FieldTypeUsed by
idstringget_note (note_id)
localRecordingIdintegerget_note_insights, get_note_labels, save_note_insight (local_recording_id)
localLabelIdintegerget_notes_by_label (local_label_id)

Tool reference

get_recent_notes

Lists notes and recordings, newest first.

ParameterTypeRequiredDescription
limitinteger, 1 to 50No (default 10)Number of notes to return
offsetinteger, 0 or moreNo (default 0)Number of notes to skip, for pagination
note_type"audio" | "text"NoFilter to voice recordings or written notes

Returns { notes: Note[], hasMore: boolean }.

{ "limit": 5, "note_type": "audio" }

search_notes

Keyword search across note titles, transcripts and text content.

ParameterTypeRequiredDescription
querystringYesSearch keyword or phrase
limitinteger, 1 to 50No (default 10)Maximum number of results

Returns { notes: NoteWithExcerpt[] }. Each result adds an excerpt around the first match.

{ "query": "Henderson site visit" }

get_note

Returns one note with its complete transcript or text body.

ParameterTypeRequiredDescription
note_idstringYesThe note's id

Returns a Note.

get_note_insights

Returns the AI insights saved against a note.

ParameterTypeRequiredDescription
local_recording_idstringYesThe note's localRecordingId
insight_typestringNoFilter, for example summary, action_items, sentiment, translate

Returns { insights: Insight[] }.

get_user_labels

Lists every label on the account. Takes no parameters.

Returns { labels: { localLabelId: number, name: string }[] }.

get_note_labels

ParameterTypeRequiredDescription
local_recording_idintegerYesThe note's localRecordingId

Returns { labels: Label[] }.

get_notes_by_label

ParameterTypeRequiredDescription
local_label_idintegerYeslocalLabelId from get_user_labels

Returns { notes: Note[] }.

get_subscription_status

Takes no parameters.

Returns { subscription: Subscription | null, isActive: boolean }. isActive is true when the subscription is active or trialing and not expired.

save_note_insight

Saves an insight against a note. Upserts on the combination of note, insight_type and target_language.

ParameterTypeRequiredDescription
local_recording_idintegerYesThe note's localRecordingId
insight_typeenumYesOne of the values below
contentstringYesThe insight text
target_languagestringWhen insight_type is translateLanguage code, for example es, fr, de

Allowed insight_type values:

summary, key_points, offer_tips, make_notes, action_items, meeting_report,
translate, linkedin_post, tweet, blog_post, email, sentiment, shopping_list

Returns the saved Insight.

{
  "local_recording_id": 1712,
  "insight_type": "action_items",
  "content": "- Send revised quote by Friday\n- Book second site visit"
}

Data shapes

interface Note {
  id: string                          // use with get_note
  localRecordingId: number            // use with insight and label tools
  displayName: string
  noteType: string                    // "audio" | "text"
  transcript: string | null           // audio notes
  textContent: string | null          // text notes
  duration: number | null             // seconds
  dateTime: string                    // ISO 8601
  transcriptionStatus: string | null
}

interface NoteWithExcerpt extends Note {
  excerpt: string | null              // snippet around the first match
}

interface Label {
  localLabelId: number
  name: string
}

interface Insight {
  id: string
  localRecordingId: string
  insightType: string
  content: string
  targetLanguage: string | null
}

interface Subscription {
  id: string
  provider: string                    // e.g. revenuecat, lemonsqueezy
  productId: string | null
  status: string                      // e.g. active, trialing, expired
  expiresAt: string | null            // ISO 8601, null if it does not expire
}

Errors

A failed call returns a normal tool result with isError: true and a human-readable message in content. Internal details are stripped from the message. A call without a valid session returns Authentication required.

What it can and cannot access

For IT administrators evaluating this connector.

It can read: your own notes and recordings, their transcripts and content, AI insights saved against them, your labels, and your subscription status.

It can write: one thing, an insight attached to a note you own.

It cannot access: any other user's data, Claude's memory, chat history, conversation summaries, or files you have uploaded to the assistant. It does not collect conversation data beyond what a given tool call needs to run.

Scoping: every database query is scoped to the authenticated user through Postgres row level security. A session cannot reach another account's data even if a tool were called with another account's identifiers.

Security

Authentication uses OAuth 2.0 with Dynamic Client Registration (RFC 7591). The authorization server is Supabase Auth, with a consent page hosted on parrotnotes.app.

Every request to /mcp is verified against the auth server before it reaches the MCP layer, so a token that is expired, malformed, or whose session was signed out elsewhere is rejected immediately.

SituationResponse
No bearer token401 with a WWW-Authenticate header pointing at the protected resource metadata
Token expired, malformed, or session revoked401 with error="invalid_token", which tells the client to re-authenticate
Auth server unreachable503 with no challenge, so clients retry instead of re-authenticating

Transport is HTTPS only. Tokens are accepted in the Authorization: Bearer header only.

Privacy and data retention

  • No data is used for AI model training.
  • Audio auto-delete is available in the app: the transcript is kept and the recording is discarded. This matters in compliance-sensitive industries.
  • Full policy: parrotnotes.app/privacy

Troubleshooting

The assistant asks you to reconnect repeatedly. Signing out of ParrotNotes on the web with a global scope ends every session for your account, including connector sessions. Sign in again and reauthorize the connector.

Tools return empty results. The account has no notes yet. Record or import something in the app first.

A tool call fails with a permission error. The note ID belongs to a different account. Tools only return data owned by the signed-in user.

get_note_labels or save_note_insight rejects the ID. These take the integer localRecordingId, not the string id. See Identifiers.

Support