Integrations
MimicScribe connects your meetings to the tools around them. AI agents reach them through an MCP server; macOS itself reaches them through App Intents — Spotlight, Shortcuts, and Siri; it writes a Markdown note into your Obsidian or Logseq vault; and it can POST to a webhook. All configured in Settings > Integrations, along with the URL import helper that pulls audio out of a video or podcast page.
For saving a meeting out by hand — Markdown, subtitles, JSON, CSV — see Exporting & sharing.
Automatic recording is documented here too, though it’s configured in Settings > Meeting > Automatic Recording. It connects to nothing — it watches which app takes your microphone and starts the recording for you.
Quick setup: If you’re already using an AI agent like Claude Code, Gemini CLI, or Codex, you can ask it to set this up for you. Try: “Set up the MimicScribe MCP server. Read the docs at mimicscribe.app/llms-full.txt for instructions.” Your agent will read this page and configure everything automatically.
MCP Server
The Model Context Protocol (MCP) server lets AI agents interact with your meeting data directly. Agents can search past meetings, pull action items, prepare context for upcoming meetings, and more — all without you copying and pasting.
What can agents do with it?
After meetings — ask your agent to work with what was discussed:
- “What did we decide about pricing in yesterday’s meeting?”
- “List all my action items from this week”
- “Write a follow-up email based on the client call from Monday”
- “Create Jira tickets for the action items from the planning meeting”
- “Draft a project status update from my meetings this past week”
Fix and re-export — correct who-said-what, then send the cleaned copy somewhere new, in one instruction:
- “In yesterday’s client call, ‘Speaker 2’ is Jane Okafor — rename her, then save a clean copy to my Obsidian vault”
- “All the microphone speech in the standup is me — merge it into ‘Marshall’, then re-export the summary to Notion”
- “Drop the phantom noise speaker from the interview and email me the corrected transcript”
- “‘Alice’ and ‘Bob’ are swapped in the planning meeting — fix them and update the doc I made from it”
The agent reads the meeting, applies the speaker correction, waits for MimicScribe to re-attribute and regenerate the summary, then re-reads the corrected meeting and writes it wherever you asked, with no copy-paste or manual relabeling. See Correcting speaker attribution.
Import recordings — have your agent transcribe files from disk:
- “Transcribe the recording from yesterday’s client call — it’s in ~/Downloads/client-call.m4a”
- “Import all the audio files in ~/Meetings/ and summarize what was discussed”
- “Get word-level timestamps for this podcast episode so I can cut the filler words”
Before meetings — have your agent prepare context:
- “Prep my next meeting — look up the attendees in our CRM, set the context, and pick the right summary template”
- “I have a meeting with jane@acme.com in 30 minutes. Look up our past meetings with anyone from Acme, check their website, and set the context in MimicScribe.”
- “Add our product pricing page as a reference source so the assistant has it during sales calls”
- “Add our company’s org chart and key terminology as reference context for all my meetings”
How do I tell it which meeting I mean?
Every meeting-scoped tool takes the reference you’d actually say out loud, not just a UUID. get_meeting_data, get_action_items, correct_meeting, and update_meeting all accept the same grammar:
| You pass | It resolves to |
|---|---|
| A UUID | That meeting, exactly. |
meeting://<uuid> or mimicscribe://meeting/<uuid> | The same — the URIs resources/list and the app hand out. |
latest (also last, most recent, my last meeting, …) | The newest meeting by start time. |
focus (also current, open, the meeting I have open) | The meeting the app last displayed. |
An ISO8601 date — 2026-09-01 | Every meeting that started on that local calendar day. |
| Any other text | Matched against meeting titles. |
Two rules to know before your agent leans on it:
- It does no natural-language date parsing. “Yesterday” is the agent’s job to convert — pass the ISO date. The alias lists above are matched on the whole argument, never as a substring, so a meeting genuinely titled “Last Call With Legal” stays findable by its name.
- Ambiguity is an error, never a silent pick. If a title fragment or a date matches more than one meeting, the tool returns
ambiguous_meeting_referencewith a candidate list — id, title, ISO date, local time, duration, speakers — and the instruction to ask you which one. An agent that picks the first is doing it wrong.
Deletion is the exception: update_meeting with delete: true takes a UUID and nothing else. Reading the wrong meeting wastes a turn; deleting it can’t be undone through MCP.
One more thing an interpreted reference can hide: if “latest” also matched an offline meeting the server can’t see, the response says so explicitly rather than quietly resolving to the second-newest.
Available tools
| Tool | Purpose |
|---|---|
search_meetings | Search by keyword or list recent meetings. Returns summaries and metadata. |
get_meeting_data | Get detailed data for a specific meeting — summary, transcript, action items, or all of them. On live-recorded meetings, speakers and transcript lines are tagged with the capture channel they came from — (mic) (this Mac’s microphone — you) or (remote) (system audio — other participants or played media) — so an agent can repair attribution without guessing. Each participant in the metadata is also tagged (you) (the app’s primary user) or (unnamed) (an un-attributed placeholder like “Speaker 2”), so an agent can build an attendee list and link real people without guessing who’s who. See Capture channels and Who’s who. |
get_action_items | Pull action items across multiple meetings, filtered by date or assignee. Each item carries a stable id (upsert into a task tool without duplicating). Pages with limit/offset; totalCount says how many match. |
list_speakers | List saved speaker profiles across all meetings — name, isPrimaryUser, meeting count, and last seen. Use to seed CRM contacts or find who to research before a call. |
get_meeting_context | Read the current meeting prep state — context text and the selected summary template, from any source (user input or prior agent writes). Agents must check this before setting context to avoid overwriting the user’s notes. |
set_meeting_context | Set context for an upcoming meeting — attendee roles, goals, background, deal info. Overwrites all existing context, including user-typed notes. Always call get_meeting_context first and preserve existing content. Expires after a configurable TTL (default: 3 hours). Optional template_id also selects the summary template governing the post-meeting output. |
list_templates | List every summary template — built-in (Simple, Walkthrough, By Participant, Decisions & Open Questions) and user-custom — with full section-by-section summary instructions. Call this first if you want to pick a template_id to pass to set_meeting_context. |
transcribe_with_speakers | Queue an audio or video file for ASR + on-device diarization. Returns anonymous speaker IDs (“Speaker 1”, “Speaker 2”) with word-level timestamps and preserved filler words. It makes no Gemini calls and doesn’t count against the AI cap. Use when you need raw timestamped words and speaker separation without real names. |
transcribe_attributed | Queue an audio or video file for AI speaker attribution. Returns a compact transcript with real speaker names (from voice profiles or transcript context) and an attributionSource field per speaker. It skips the summary and action items and isn’t saved to the library. Use when you want a polished script without polluting the user’s meeting history. |
import_meeting | Queue an audio or video file for full meeting import. Runs the complete pipeline (diarization + attribution + summary + action items) and persists the result to the user’s library. Optional offline: true skips Gemini and saves as offline mode — the user can backfill via “Generate Summary Now”. Use when the user wants this saved as a meeting. |
get_import_status | Check the status of recent file imports, corrections, and meeting updates. Returns a unified recentResults array with type discriminator (transcription / attributed_transcript / meeting / correction / meeting_update), plus recentFailures and recentRejections. Call after a notification to see what happened. |
get_transcript_result | Retrieve a completed transcribe_with_speakers or transcribe_attributed result by id. One getter for both — the result id carries no store marker, so you don’t have to remember which tool you queued. Returns {type, result}; pass format for a ready-to-save document instead of JSON. |
set_context_source | Manage persistent reference documents — add, replace, or remove sources (URLs, files, or raw text) that are semantically retrieved during meetings. Sources are chunked, embedded, and matched to what’s being discussed. |
correct_meeting | Fix speaker attribution on a completed meeting — rename, merge, swap, or remove a speaker’s turns. The app re-validates the change, applies it, and regenerates the summary. See Correcting speaker attribution. |
update_meeting | Edit a meeting’s library metadata: rename it (title), or delete it (delete: true). Exactly one action per call. Works on offline meetings when offline access is enabled. Deletion is permanent with no MCP undo — confirm with the user first. Queued and applied by the app; poll get_import_status (type meeting_update). |
delete_meeting | Permanently delete a meeting from your library. Strict privilege separation from update_meeting: requires the meeting’s exact UUID (never interpreted references like latest) and explicit confirm: true. Deletion is permanent with no MCP undo — confirm with the user first. Queued and applied by the app; poll get_import_status (type meeting_update). |
write_meeting_summary | Write or overwrite an AI-generated meeting summary, key decisions, overview, and action items. Preserves pre-edit restore points in the change ledger, updates SQLite FTS5 search, and regenerates local on-device vector embeddings. Stamped with summarySource: "agent". Queued and applied by the app; poll get_import_status (type summary_write). |
update_action_items | Update existing action items (lifecycle state: open, completed, dismissed; task text, owner, due date) or append new action items without rewriting the entire summary. Preserves pre-edit restore points in the change ledger, updates FTS5 search, and re-indexes vector embeddings. Queued and applied by the app; poll get_import_status (type action_items_update). |
Capture channels (mic / remote)
Every live-recorded meeting captures two separate audio channels: your microphone and the system audio (everyone else on the call, plus any media you play). MimicScribe keeps that provenance and surfaces it to agents on get_meeting_data, where speakers and transcript lines carry a channel tag. (get_transcript_result exposes the same information as an optional channel field, but only for dual-channel sources — the files the two transcription tools take today are single-channel, so it’s empty there.)
The tags are:
mic— this Mac’s microphone: the device owner (you).remote— system audio: other participants, or played-back media.mixed— the segment spans both channels or its provenance couldn’t be resolved. Treat as unknown — onlymicandremoteare reliable evidence.
This comes straight from the recording hardware, so an agent can repair attribution with zero AI calls. The canonical example: “everything on the mic channel is the same person — me,” turned into a merge via correct_meeting. It also disambiguates a name the AI attributed to the wrong side.
Tags are omitted where the label already names its channel (the diarizer’s own "Mic 1", "Remote 2" families) — so a tagged line is one whose speaker was renamed or AI-attributed, i.e. exactly where the channel would otherwise be invisible. File imports are single-channel and carry no tags — there’s no separate mic/system stream in an imported file.
Who’s who: you and unnamed
On top of the channel tag, get_meeting_data’s participant list marks two things an agent would otherwise have to guess:
(you)— the app’s primary user. Exclude this person when building a list of other attendees, like a CRM contact list.(unnamed)— a placeholder the diarizer assigned that nobody renamed, likeSpeaker 2orRemote 1. Not a real identification — don’t link it or file it as a contact.
A participant with neither tag is an attributed real name. In this line:
**Speakers:** Jane Okafor (remote), Marshall (mic, you), Speaker 2 (remote, unnamed) Jane is an attendee to link, Marshall is you, and “Speaker 2” is unresolved. These tags are what let an agent file a meeting into another tool without asking you who each speaker was.
Filing meetings into your tools
For Notion, your CRM, task managers, and most other tools, your agent does the filing — using what these tools return. There’s no per-tool connector to configure; the mapping below is what a capable agent needs to do the filing itself. (Two things MimicScribe can do without an agent: write Markdown notes into an Obsidian vault, Logseq graph, or any folder — see Markdown notes folder — and POST each meeting to a URL for Zapier/Make/n8n — see Webhook.)
Two output shapes carry the material. get_meeting_data returns Markdown:
# Q3 Planning Sync
**Date:** Jul 16, 2026 at 2:30 PM
**Duration:** 45m
**Speakers:** Jane Okafor (remote), Marshall (mic, you), Speaker 2 (remote, unnamed)
**Status:** completed
**Tags:** pricing, roadmap
## Summary
The team agreed to ship the revised pricing before the board review...
## Action Items
- **Marshall**: Send the revised pricing deck
- Due: 2026-07-20 get_action_items returns the same items as structured JSON, across one or many meetings:
{
"actionItems": [
{
"owner": "Marshall",
"task": "Send the revised pricing deck",
"due": "2026-07-20",
"type": "task",
"confidence": "firm",
"meetingId": "…",
"meetingTitle": "Q3 Planning Sync",
"meetingDate": "2026-07-16T14:30:00Z"
}
],
"returnedCount": 1
} Obsidian. You don’t need an agent for this one — MimicScribe can write a note into your vault automatically (Markdown notes folder below). An agent is still useful for retroactive filing (backlinking a meeting to a contact you’re researching right now) or one-off exports outside your normal vault location: pull get_meeting_data and write the Markdown yourself — date and duration become YAML front-matter fields, participants tagged as real names become [[wikilinks]] (skip (you) and (unnamed)), and the summary and action items are the body.
Notion. Same data, filed as a database page: map date, participants, and tags to database properties, and render the summary and action items as blocks (action items as to-do blocks). Do this through the Notion API — Notion’s plain-Markdown import flattens the structure.
CRM (HubSpot, Attio, Salesforce). The participant tags do the work here. Every participant except your (you) entry and any (unnamed) placeholder is a real attendee — map them to contacts, log the summary as an activity note on the account, and turn get_action_items into follow-up tasks carrying their owner and due date.
Task managers and Apple Notes. For tasks, skip the transcript entirely and pull get_action_items: each item already carries an owner, a due date, and a firm / tentative confidence — send the firm ones to Things, Todoist, or Reminders and drop the tentative ones unless you want maybes. For Apple Notes, paste the summary rather than raw Markdown; Notes renders rich text, so the prose lands clean and the ## headings don’t show through.
Resources
The server also exposes meetings as MCP resources. Calling resources/list returns the 20 most recent meetings as meeting://{id} URIs. Calling resources/read with a URI returns the meeting content:
meeting://{id}— full transcript as markdownmeeting://{id}/summary— AI-generated summarymeeting://{id}/action-items— structured action items as JSONmeeting://{id}/segments— structured transcript as JSON (speakers + timestamped segments), for importers that can’t parse prose
Resources are read-only. Most agents will find the tools (search_meetings, get_meeting_data) more convenient, but resources provide an alternative access pattern for MCP clients that prefer it.
Notifications
The MCP server sends notifications/resources/list_changed when meetings change — new meetings ready, imports completed or failed, meetings deleted or merged. After receiving a notification, agents should determine what changed:
- For imports: call
get_import_statusto see outcomes (success with meeting ID, rejected, or failed with reason) - For everything else: call
search_meetingsto see the current meeting list, orresources/listto re-fetch resources
The notification includes structured details in its params for MCP clients that support reading them. Not all clients do — the tool-based approach above works universally.
When notifications fire:
| Event | When | How to check |
|---|---|---|
| Live meeting ready | User closes the summary view (after any speaker corrections) | search_meetings to find new meeting |
| Meeting import succeeded | import_meeting enrichment completes | get_import_status → recentResults (type==“meeting”) → get_meeting_data |
| Diarized transcript ready | transcribe_with_speakers finishes | get_import_status → recentResults (type==“transcription”) → get_transcript_result |
| Attributed transcript ready | transcribe_attributed finishes | get_import_status → recentResults (type==“attributed_transcript”) → get_transcript_result |
| Import rejected | Billing cap reached before processing | get_import_status → recentRejections for reason |
| Import failed | Transcription or enrichment errored | get_import_status → recentFailures for reason |
| Meeting deleted | User deletes a meeting | search_meetings to see updated list |
| Meetings merged | User merges multiple meetings | search_meetings to see updated list |
| Correction applied | correct_meeting request finished (applied, partial, or failed) | get_import_status → recentResults (type==“correction”) → get_meeting_data |
Notification params (for custom MCP clients): The list_changed notification carries optional params with arrays describing what changed. Presence of each key indicates the event type:
meetings[]—{id, title, sourceFilename?}— meeting ready (live recording orimport_meetingcompletion)transcriptions[]—{filename, resultId}—transcribe_with_speakerscompleted (useget_transcript_resultwithresultId)attributedTranscripts[]—{filename, resultId}—transcribe_attributedcompleted (useget_transcript_resultwithresultId)rejections[]—{filename, reason}— import blocked before processingfailures[]—{filename, reason}— import errored during processingdeleted[]—{id, title}— meeting removedmerged[]—{id, title, replacedIds[]}— meetings combined into onecorrections[]—{meetingId, title?, requestId, status, reason?}— acorrect_meetingrequest finished (status:applied/partial/failed)
Standard MCP clients that ignore notification params can use the tool-based fallback above. These params are an optimization for clients that can parse them.
File import
Agents can import audio and video files for processing. The MimicScribe app must be running — the MCP server queues the request, and the app picks it up. Each of the three tools takes either a local path or an https url (the app downloads it, then processes it the same way). Three tools cover three distinct use cases:
| Tool | Pipeline | Library | Gemini | Use for |
|---|---|---|---|---|
transcribe_with_speakers | ASR + on-device diarization (anonymous IDs, fillers preserved) | No | No | Raw timestamped words with speaker separation (subtitle gen, alignment, agent QA) |
transcribe_attributed | ASR + diarization + AI speaker attribution (no summary) | No | Yes | Polished named-speaker transcript without saving to the library |
import_meeting | Full pipeline + persisted to library (or offline: true to defer) | Yes | Yes* | Anything the user wants saved as a meeting in their MimicScribe library |
* import_meeting with offline: true skips Gemini and saves the meeting in offline mode — the user can backfill the summary later via “Generate Summary Now”. Use this for the cap-exhaustion recovery path.
Each write tool returns immediately with { status: "queued", requestId, filename } (plus offline echo for import_meeting). The app processes the file asynchronously, sends a notifications/resources/list_changed when complete, and exposes the result through get_import_status. Match queued requests to results by filename + createdAt ordering.
Workflow: fetching a transcription result
Both transcription tools use the same three steps and the same getter:
- Call
transcribe_with_speakersortranscribe_attributedwith the path - On notification (or polling), call
get_import_status— look inrecentResultsfortype=="transcription"ortype=="attributed_transcript" - Call
get_transcript_resultwith theresultId
There is one getter because a result id is a bare UUID with no store marker in it, and “I called the wrong getter for the tool I queued” was a routine failure when there were two. get_transcript_result tries both stores and tells you which one answered:
{ "type": "transcription" | "attributed_transcript", "result": { … } } type echoes the pipeline that produced the payload, so an agent doesn’t have to infer it from which fields happen to be present. Pass format (markdown, txt, srt, vtt, json, csv) to get a ready-to-save document instead of the JSON envelope — JSON is the only shape carrying per-word data or attribution provenance, so prefer it when you need either.
type: "transcription" — from transcribe_with_speakers:
{
"filename": "interview.m4a",
"durationSeconds": 1823.5,
"createdAt": "2026-03-29T10:00:00Z",
"text": "hello um welcome to the show today...",
"speakers": [
{ "id": "Speaker 1", "totalDurationSeconds": 1102.3 },
{ "id": "Speaker 2", "totalDurationSeconds": 721.2 }
],
"segments": [
{
"speakerId": "Speaker 1",
"start": 0.24,
"end": 2.8,
"text": "hello um welcome to the show",
"words": [
{ "word": "hello", "start": 0.24, "end": 0.56, "confidence": 0.97 },
{ "word": "um", "start": 0.64, "end": 0.88, "confidence": 0.85 },
{ "word": "welcome", "start": 1.04, "end": 1.52, "confidence": 0.99 }
]
}
]
} Speaker IDs are always anonymous ("Speaker 1", "Speaker 2", …) — they are never real names. Filler words (um, uh, like) are preserved. Payload is roughly 80–120 KB per hour of audio.
type: "attributed_transcript" — from transcribe_attributed:
{
"filename": "standup.mp4",
"durationSeconds": 940.2,
"createdAt": "2026-03-29T10:15:00Z",
"speakers": [
{
"name": "Alice",
"attributionSource": "voiceProfile",
"totalDurationSeconds": 412.6
},
{
"name": "Bob",
"attributionSource": "transcript",
"totalDurationSeconds": 327.4
},
{
"name": "Speaker C",
"attributionSource": "fallback",
"totalDurationSeconds": 200.2
}
],
"transcript": [
{
"speaker": "Alice",
"start": 0.5,
"end": 12.3,
"text": "Hi everyone — let's start with the deploy status."
}
]
} attributionSource reports how each speaker name was derived:
voiceProfile— matched a registered voice profile in MimicScribe (highest confidence)transcript— name inferred from speech context (someone addressed by name)fallback— no identification possible; the label is generic (e.g., “Speaker C”)
Always surface attributionSource to users for fallback speakers — those names are not real identifications. Filler words are filtered on this path. Payload is roughly 8–12 KB per hour, safe to pass through agent context even for multi-hour recordings.
Degraded mode. If no AI provider was reachable, the result carries attributionDegraded: true and a degradedReason, the speakers are anonymous cluster labels, and the text is on-device rather than model-cleaned. A normal result omits both fields. Name the speakers from context yourself rather than presenting the cluster labels as identifications.
Workflow: import_meeting
- Call
import_meetingwith the path (andoffline: trueif the user wants to skip Gemini) - On notification (or polling), call
get_import_status— look fortype=="meeting" - Call
get_meeting_datawith themeetingIdto read the summary, transcript, and action items
import_meeting with offline: false runs the full pipeline (the same one used for live meetings) and counts against the AI cap. With offline: true, the meeting saves with diarization only and skips Gemini — useful when the cap is exhausted or the user has opted out of AI for this meeting. The user can backfill the summary later by tapping “Generate Summary Now” in the app.
Result lifecycle
transcribe_with_speakers and transcribe_attributed results are ephemeral — stored for 24 hours, then garbage-collected. No meeting is created in the user’s library; if get_transcript_result comes back “not found”, the result has expired (or the id was never issued) and the agent should re-queue with the original write tool. A malformed id gets its own distinct error rather than being reported as an expiry, so an agent doesn’t go re-queue work that was never queued.
import_meeting results are permanent — the meeting lives in the user’s library and is searchable via search_meetings. Offline meetings are hidden from MCP tools by default unless the user enables “Allow connected AI tools to access offline meetings” in Settings > Integrations. When enabled, connected AI tools can read offline meetings and change them: rename and delete (update_meeting, delete_meeting — deletion requires an exact UUID), correct speakers and facts (correct_meeting, which skips cloud summary regeneration for offline meetings), write a summary (write_meeting_summary), and update action items (update_action_items). With the setting off, those calls return the local_mode_meeting error code. MimicScribe never sends offline audio or transcripts to its own cloud features regardless of this setting.
Billing & caps
MCP tool calls are unlimited on every plan until January 1, 2027; from then the Free tier gets 100 calls per week (resets Monday; Pro and paid plans are unlimited). The per-feature limits below also apply:
| Tool | Counts against AI cap? | Free-tier weekly limit (shared bucket) |
|---|---|---|
transcribe_with_speakers | No — on-device only | No cap |
transcribe_attributed | Yes — one AI call | 20 file transcriptions/week, shared with import_meeting |
import_meeting | Yes (attribution + summary) | 20 file transcriptions/week, shared with transcribe_attributed |
import_meeting (offline) | No — AI skipped | No AI-cap charge |
The file-transcription bucket is separate from the MCP-calls cap — from January 1, 2027 a file import draws on both.
correct_meeting counts as one call against the shared MCP cap (paid plans are unlimited). It doesn’t draw the file-transcription bucket, but the summary regeneration it triggers runs through the meeting AI provider like any other meeting summary.
If import_meeting is rejected due to a cap, retry the same path with offline: true — the meeting saves without Gemini and the user can backfill the summary later.
Shared details
Supported formats: M4A, WAV, MP3, MP4, MOV, and any format readable by AVFoundation. Minimum 3 seconds, maximum 4 hours of audio. The file path must be absolute and accessible to the MimicScribe app — files on the local disk work; files on network volumes may not.
Meeting processing status values (import_meeting only — the two transcription paths complete in one step):
| Status | Meaning |
|---|---|
pending_enrichment | Diarized; Gemini summary pending |
processing | Gemini enrichment in progress |
completed | Fully processed — transcript, speakers, summary, and action items available |
offline_mode | Saved without AI — user can backfill via “Generate Summary Now” in the app |
billing_blocked | Transcript saved but Gemini enrichment requires an active subscription |
Queueing: Multiple files can be queued at once — they process sequentially across all three tools (only one Neural Engine task runs at a time). If a meeting is being recorded, queued imports pause and resume automatically when the meeting ends. Agents can poll get_import_status if they don’t support notifications.
Correcting speaker attribution
correct_meeting is the one MCP tool that writes back to a saved meeting. Everything else reads or queues new work — this fixes an existing one. It closes the loop behind the Fix and re-export prompts above: an agent can read a meeting, correct who-said-what, and hand you (or another app) a cleaned copy without you touching the app.
The agent never edits the transcript directly. It proposes a small list of operations; MimicScribe re-validates each one against the meeting’s current speaker roster and applies it deterministically, then regenerates the summary and action items so they reflect the new names.
Operations:
| Kind | Fields | Effect |
|---|---|---|
rename | from, to | Give a speaker a real name. If to is already a speaker, this becomes a merge (flagged in the response). |
merge | from, to | Fold one speaker’s turns into another — e.g. two mic clusters that are both you. |
swap | from, to | Exchange two speakers’ labels in one step (use this instead of a temp-name shuffle). |
remove | target | Delete a speaker’s turns entirely — a phantom or noise “speaker” the diarizer invented. |
Labels must match what get_meeting_data returns (case-insensitive). Combined with channel tags, the agent has everything it needs to fix attribution — often without an AI call of its own.
Workflow:
- Call
get_meeting_datato read the current roster (and channel tags). - Call
correct_meetingwith the meetingidand anoperationsarray. It returns immediately with{ status: "queued", requestId }after pre-validating every op. - On the correction notification (or by polling
get_import_statusfortype == "correction"with yourrequestId), check the status:applied— done; re-fetch withget_meeting_datato see the corrected transcript and summary.partial— the speaker changes landed but the summary regeneration failed. Do not resubmit the operations (they already applied); the summary is stale until the user regenerates it in the app.- a failure appears in
recentFailures.
- Re-read the corrected meeting and do whatever the user asked next — export it, email it, file tickets.
Rules and guardrails:
- One request at a time per meeting. All ops in a single request resolve against the same roster snapshot — chained renames or temp-name staging in one batch will not work. For exchanged labels, use
swap. Wait for the outcome before the next request. - Merges and removes are destructive. The user can undo them in the app, but there is no undo through MCP — confirm intent with the user before a destructive op.
- Renames can follow the voice forward. Pass
enrollVoiceProfile: true(defaultfalse) to also update the saved voice profile, so the name attaches to that voice in future meetings. This is not covered by the in-app undo — only set it once the user has confirmed the name. - “My mic is always me” locks the mic channel. If the meeting was recorded with that setting on, renaming, swapping, removing, or merging the mic speaker into someone else is refused with
mic_locked. Merging another speaker into the mic speaker is allowed. - Not for wording or factual fixes.
correct_meetingonly moves speaker labels around. It deliberately rejects transcript-wording edits, factual summary corrections, and “there was really only one speaker” — those have unbounded blast radius. If the user is exporting the meeting, apply that kind of fix in the exported copy and tell them the stored meeting is unchanged. To fix the stored meeting itself, they type the correction into that meeting’s Q&A box in MimicScribe. - Offline meetings are corrected without a summary refresh. With “Allow connected AI tools to access offline meetings” on, speaker corrections apply to offline meetings too. The summary isn’t regenerated for them, so an existing one keeps its wording.
Errors are structured — { error: <code>, message } — so agents branch on the stable code, not the prose. Codes: invalid_meeting_id, invalid_operations, unsupported_kind, provider_off, meeting_not_found, local_mode_meeting, not_completed, op_unbound, would_empty_transcript, mic_locked. On op_unbound the message echoes the current roster, so an agent can correct the labels and retry in one turn. Every op is validated before anything changes — a request either applies in full or fails whole, never halfway.
Meeting prep
Agents can set context before a meeting starts, enriching the AI assistant with background information it wouldn’t otherwise have. Agent-set context and user-typed prep notes (from the app’s “Prepare for Meeting” feature) share the same store — both are visible to agents and to the user.
A typical agent workflow:
- Call
get_meeting_contextto check for existing content (could be user prep notes or another agent’s context) - Research attendees (from a CRM, email threads, past MimicScribe meetings, etc.)
- To shape the post-meeting summary, either call
list_templatesand pick atemplate_id(a saved summary view — e.g. Walkthrough or By Participant), or passsummary_formatfree-text rules to shape this one meeting without a saved view - Call
set_meeting_context— preserve any existing content verbatim at the top, append your additions below, and passtemplate_idorsummary_formatto shape the summary - When the user starts their next meeting, MimicScribe automatically injects the context and applies the chosen view (or formatting rules) to the summary
Important: set_meeting_context overwrites the entire context slot. If the user has already typed prep notes in the app and your agent doesn’t call get_meeting_context first, those notes will be lost. Agents should always read before writing.
View and format semantics on re-writes: When you omit template_id (or summary_format), any previously-staged value is preserved — a safe default for get → append → set flows. Pass an empty string to explicitly clear it and fall back to the global default. A template_id that isn’t a known view returns an error; always source ids from list_templates rather than hardcoding. If both template_id and summary_format are set, the saved view wins. A view the user picks in the app is always respected at meeting start — agent-staged values only apply when the user hasn’t made a pick.
Context set by agents expires after 3 hours by default (configurable via ttl_minutes, max 24 hours). Prep notes saved by the user via the app do not expire. The maximum context size is 50,000 characters. Only one slot of context exists at a time — setting new context overwrites whatever was there. The context is cleared when a meeting ends and won’t carry over to subsequent meetings.
Reference documents
Agents can manage persistent reference documents that are semantically retrieved during meetings. Unlike meeting prep context (which is consumed once and expires), reference documents persist across all meetings and are automatically matched to what’s being discussed.
Use set_context_source to add URLs, local files, or raw text as reference sources. The app chunks, embeds, and indexes each source — during meetings, the most relevant chunks are injected into AI prompts based on the conversation.
A typical agent workflow:
- Call
set_context_sourcewithoperation: "upsert", atitle,source_type, andcontent - The source appears immediately in the response. Processing (chunking + embedding) takes 5-15 seconds.
- Call with
operation: "list"to check status — look forstatus: "ready"
Source types:
| Type | Content |
|---|---|
url | A web page URL — the app fetches and processes the page content |
file | An absolute local file path — the app reads and processes the file |
text | Raw text provided inline — best for company info, org charts, or content from any source |
What makes good reference context: company background, product documentation, org charts, competitive landscape, key terminology glossaries, project briefs, or prior meeting summaries. Structure text with markdown headers — headers become the semantic search keys, so make them descriptive.
Sources are managed by title (case-insensitive). Upserting with an existing title replaces that source. Maximum 10 sources, up to 200,000 characters each. Sources added through the app UI can be viewed by agents but can only be removed through the app.
Reference documents vs. prep notes: Use set_context_source for persistent material reused across many meetings (product docs, org charts, glossaries). Use set_meeting_context for per-meeting prep notes that should expire (today’s attendees, agenda, deal stage). For best results, use both.
Configuration
Open Settings > Integrations in MimicScribe and click Copy Configuration to get the MCP config JSON for your agent client. The configuration looks like this:
{
"mcpServers": {
"mimicscribe": {
"command": "/Applications/MimicScribe.app/Contents/MacOS/mimicscribe",
"args": ["--mcp-server"]
}
}
} The MCP Server card has a master switch. It’s on by default. Turn it off and the server still starts and lists its tools to a connecting agent, but every request to read or import a meeting is refused with a message telling the agent (and you) that MCP is turned off in Settings — no meeting content leaves your Mac. A freshly connecting agent sees the change immediately; an agent with a long-lived connection open may take a few seconds to pick it up (or reconnect to be sure). No app restart is needed either way.
CLI agents can be configured with a single command:
| Agent | Command |
|---|---|
| Claude Code | claude mcp add mimicscribe -- /Applications/MimicScribe.app/Contents/MacOS/mimicscribe --mcp-server |
| Gemini CLI | gemini mcp add mimicscribe /Applications/MimicScribe.app/Contents/MacOS/mimicscribe --mcp-server |
| Codex CLI | codex mcp add mimicscribe -- /Applications/MimicScribe.app/Contents/MacOS/mimicscribe --mcp-server |
For Gemini CLI, if the server shows as “Disconnected,” run gemini trust in your working directory.
Codex also supports adding to ~/.codex/config.toml:
[mcp_servers.mimicscribe]
command = "/Applications/MimicScribe.app/Contents/MacOS/mimicscribe"
args = ["--mcp-server"] Desktop and IDE agents — paste the JSON config above into the appropriate settings file:
| Agent | Config location |
|---|---|
| Claude Desktop | claude_desktop_config.json (Claude menu > Settings > Developer > Edit Config) |
| Cursor | ~/.cursor/mcp.json or .cursor/mcp.json in your project |
| Windsurf | ~/.codeium/windsurf/mcp_config.json or Windsurf Settings > Cascade > MCP Servers |
| Zed | ~/.config/zed/settings.json (use key "context_servers" instead of "mcpServers") |
VS Code (GitHub Copilot) uses a slightly different format in .vscode/mcp.json:
{
"servers": {
"mimicscribe": {
"type": "stdio",
"command": "/Applications/MimicScribe.app/Contents/MacOS/mimicscribe",
"args": ["--mcp-server"]
}
}
} Requires VS Code 1.99+ and Copilot Agent Mode.
Any other MCP client that supports stdio transport will work — use the JSON config from Settings > Integrations.
Remote access via SSH
The MCP server uses stdio (standard input/output), which means it runs locally by default. If you need to access your meetings from another machine — for example, from a cloud-hosted agent or a remote workstation — you can use SSH:
ssh user@your-mac.local "/Applications/MimicScribe.app/Contents/MacOS/mimicscribe --mcp-server" Configure your MCP client to use this SSH command instead of the local binary path. The SSH connection handles authentication and encryption — no additional security configuration is needed.
Requirements: Enable Remote Login on your Mac (System Settings > General > Sharing > Remote Login). The MimicScribe app must be installed at the path specified in the command.
Troubleshooting MCP
Agent can’t connect or shows “server exited”: MimicScribe needs to have been launched at least once to create its database. Open the app, then restart your agent client.
Agent can’t find any meetings: Make sure you’ve recorded at least one meeting. The MCP server reads from the same database as the app.
Agent shows stale data: The MCP server reads the database directly, so data is always current. For live meetings, the server waits to notify agents until you close the summary view — so if you’re still reviewing or correcting speakers, the agent won’t have the latest version yet. Close the summary when you’re done to trigger the notification.
Wrong binary path: If you moved MimicScribe from /Applications/, update the command path in your MCP configuration. You can find the correct path in Settings > Integrations.
App Intents
MimicScribe exposes your meetings to macOS itself through Apple’s App Intents: they show up in Spotlight, and you can drive recording and pull up meetings from Shortcuts and Siri. There’s nothing to wire up — the Shortcuts and Siri actions are available as soon as the app has launched once, and Spotlight indexing is on by default.
This is a local integration. MimicScribe doesn’t send your meeting data anywhere for it — the Spotlight index is an on-device macOS index, and the Shortcuts and Siri actions run against your local library. (If you invoke an action by voice, Siri’s own speech processing is Apple’s, not MimicScribe’s.)
Meetings in Spotlight
With Spotlight indexing on (the default), every finished meeting is searchable straight from ⌘Space — and not just by title. MimicScribe indexes the title, the summary, the meeting’s tags, action-item owners, speaker names, and the full transcript text, so you can find a meeting by something that was said in it. Click a result to open the meeting in MimicScribe.
- Offline meetings are indexed too — a local, on-device transcript is exactly what Spotlight should be able to find.
- Only unfinished meetings are left out — anything still processing or permanently failed has no transcript to index yet. Everything else, online or offline, is in.
- The index is on-device, never expires, and deleting a meeting removes it.
- A very long meeting has the first 100,000 characters of its transcript indexed for Spotlight. This is a Spotlight-index bound only; search inside MimicScribe reads the whole transcript.
Control it in Settings > Integrations, under the Spotlight, Shortcuts & Siri card — the Show meetings in Spotlight switch. It’s on by default; turning it off removes every meeting from the index. This switch only affects Spotlight — the Shortcuts and Siri actions below keep working either way.
Shortcuts and Siri
MimicScribe ships seven actions. Find them in the Shortcuts app under MimicScribe, or trigger the voice-enabled ones with Siri:
| Action | What it does | Siri |
|---|---|---|
| Record Meeting | Start a recording (optional title). Opens the app. | Yes |
| Stop Recording | Stop and save the current recording. | Yes |
| Toggle Meeting Recording | Start if idle, stop if recording. | Yes |
| Search Meetings | Return meetings matching a keyword, optionally scoped to a date range. Uses the same search as the app. | Yes |
| Get Action Items | Return a meeting’s action items (defaults to your most recent; tentative items excluded unless you opt in). | Yes |
| Get Meeting Summary | Return a meeting’s summary (defaults to your most recent). | Shortcuts only |
| Open Meeting | Open a specific meeting in the app. | Shortcuts only |
Siri fills in the app name, so phrases look like:
- “Record a meeting with MimicScribe” · “Start recording meeting in MimicScribe”
- “Stop recording in MimicScribe” · “Finish recording in MimicScribe”
- “Toggle meeting recording in MimicScribe”
- “Search meetings in MimicScribe” · “Find meetings in MimicScribe”
- “Get action items from MimicScribe” · “What are my action items in MimicScribe”
Open Meeting and Get Meeting Summary take a specific meeting as input, so a blind Siri phrase can’t tell which one you mean — they’re reachable from the Shortcuts app, where you pick the meeting, and are built to chain (e.g. Search Meetings → Get Meeting Summary → append to a note). Like the app’s own window, all of these actions can see every meeting in your library, including offline ones.
Automations to try
Because these are real App Intents, you can chain them in the Shortcuts app or fire them from an Automation — a time of day, a calendar event starting, a Focus turning on, or a tap on your Home Screen or Apple Watch. A few that people find useful:
- One-tap record. A single-action shortcut that runs Toggle Meeting Recording, pinned to the menu bar, your Home Screen, or an Apple Watch complication — start and stop without reaching for the app.
- Morning action-item briefing. A daily Automation that runs Get Action Items on your most recent meeting and reads the result aloud or drops it into Reminders — your follow-ups, before the day starts.
- Meeting → note. Chain Search Meetings → Get Meeting Summary → Append to Note (or a Slack message, or an email) to file a recap wherever your team lives, right after the call.
- Hands-free start. Trigger Record Meeting from a Focus mode or a calendar event so recording begins the moment your meeting does — or just say “Record a meeting with MimicScribe.”
- Ask Siri for the recap. “What are my action items in MimicScribe” reads your latest follow-ups back to you, no app switch.
Every action reads or controls your on-device library, so these run without sending your meetings anywhere.
Automatic recording
Beta. This is the newest part of MimicScribe and the part most likely to change. It recognises three native apps and a list of meeting sites, and anything it does not recognise it leaves alone. Please report a call it misses, or anything it starts that was not a meeting.
MimicScribe can notice when you are in a call and catch it from the start. It lives in Settings > Meeting > Automatic Recording, not under Integrations, because it does not connect to anything: it watches your Mac locally and decides whether to record.
Nothing is armed until you turn on the master switch at the top of the pane, and nothing listens before a recording is running.
While an automatic recording is running, the menu bar shows “Capturing audio” with the elapsed time. MimicScribe does not record you silently, and that is the signal — an automatic start deliberately never steals focus or opens a window, so the menu bar is where you check. To throw one away, open MimicScribe and discard it from the meeting; discarding deletes the audio and the database row together.
Apps
Three, and only three: Microsoft Teams, Slack, and Zoom. Each is recognised by a signal that was measured on a real call, with an idle control to prove it does not fire when the app is merely open. Teams and Slack are detected from a power assertion macOS exposes while a call is up; Zoom from the helper process it runs only during a meeting.
Two consequences of that standard:
- These apps need no Accessibility permission. macOS tells MimicScribe the app is in a call, so nothing has to read your screen or your windows.
- An app that is not on the list does nothing at all. It will not record and will not prompt you. The list is the complete set of apps that can act, which is what makes it a list worth reading.
Apps that used to appear here and no longer do: Webex and FaceTime, which provided no signal that distinguished a call from being open; Discord, whose only signal fires the moment you join a voice channel and sit silently; and Teams classic, which was never watched on a real call. A row that cannot fire, or that nobody verified, is worse than no row, because in the pane it looks identical to one that works. That is about the native apps only — Webex in a browser is still covered, as one of the default Meeting Sites below.
Meeting Sites
For calls in a browser, the unit is the meeting service, not the browser: Google Meet, Zoom, Teams, and so on. A site matches on its address, and optionally on words in the window title for a service whose host you cannot predict. You can add your own.
Seven sites ship on by default: Google Meet, Zoom, Microsoft Teams, Webex, Slack huddles, Whereby, and Around. Webex is on this list
even though it was dropped from the Apps list above — the two are separate
questions. The native Webex app gave no signal that told a call apart from being
open; webex.com in a browser is matched by address like any other site, and the
browser’s own live-call signal supplies the rest.
Two of those seven can never fire on their address alone. app.slack.com is all
of Slack and around.co is a product website, so for those the window title has
to confirm (“Huddle”, “Around”) before anything can start. A custom site you add
yourself is matched on its address — adding it is itself the statement that the
host is meeting-shaped.
Recording a browser call needs both a matched site and evidence that a call is actually live, so a Meet tab left open in the background does not trigger anything.
Sites need Accessibility permission and apps do not, which is why Apps comes first in the pane. Decline the permission and the three apps above keep working; only site matching goes quiet. Firefox is unsupported for site matching.
Stopping
An automatic recording stops on its own when the call ends, after five minutes of silence on both sides, or when your Mac sleeps.
How quickly “the call ended” counts depends on which signal started it, because each signal ends differently and they are not equally trustworthy. One number can’t cover all of them:
| How the call was detected | Stops after | Why that number |
|---|---|---|
| Safari, and the calling tab was closed | ~3 seconds | A closed tab has nothing to come back with, so waiting is pure delay. Still three polls, not one, because a single failed read of the tab list was seen mid-call. |
| Microsoft Teams, Slack, Zoom — an app signal disappearing | ~5 seconds | These signals track the call itself: they appear on join, vanish on leave, and don’t blink while a call is healthy. |
| Chrome, Brave, Edge, Arc, Vivaldi — the browser’s call signal ends | ~5 seconds | Same signal class as the apps. A single flaky read resets the timer rather than spending it, so what’s being waited out is a signal confidently absent five polls running. |
| Safari, tab still open but no longer calling | ~5 seconds | The tab could start calling again, so it gets the full grace rather than the closed-tab shortcut. |
A recording started by automatic recording always ends on one of the rows above, or on the silence and sleep backstops. Firefox is the one browser with no measured signal at all, so automatic recording never starts a call there in the first place.
Five seconds is a judgment call. Nobody has measured it. It has to survive a genuine reconnect, where a wifi blip tears a call down and rebuilds it, and nobody has killed wifi mid-call and timed how long the signal stays away. It sat at fifteen seconds for a day, and at forty-five before that; users said forty-five felt broken, twice, so too long has a cost too.
If a call drops and comes back within a couple of minutes, the recording that follows is treated as a continuation rather than a fresh meeting, so a reconnect does not cost you the tail of your call.
A meeting you started yourself is never stopped for you, however long it goes quiet: a break in a workshop recording should not end the recording. Stopping one is always your call.
Two cleanup rules:
- An automatic recording you discard, or one that auto-stopped with under two minutes of audio, is deleted completely: audio and database row. That is the escape hatch for a false positive.
- A Stop you tap yourself always saves, however short. You said yes; that wins.
Markdown notes folder (Obsidian, Logseq, or any folder)
MimicScribe can write a Markdown note for every meeting straight into a folder on your Mac — no agent, no MCP client, nothing to script. Turn it on in Settings > Integrations > Markdown notes folder, point it at a folder, and every meeting from then on gets a file.
This is a purely local file write — the note’s delivery never touches the network. What the note contains depends on how you generate summaries (see What each lane sends).
Setup
- Open Settings > Integrations and find the Markdown notes folder card.
- If MimicScribe finds an Obsidian vault or a Logseq graph on your Mac, it lists it under Quick setup as a button labeled
Obsidian: <vault name>orLogseq: <graph name>— click it and the folder fills in (<vault>/Meetingsfor Obsidian,<graph>/pagesfor Logseq). Obsidian is read from its own local vault list; Logseq has no such list, so detection is a best-effort check of the default graph locations (~/Documents/logseq,~/logseq, the iCloud Logseq folder) — nothing is sent anywhere to detect either. - No vault or graph detected, or want a different location? Pick or paste any folder path directly. For a Logseq graph that wasn’t auto-detected, point at its
pagesfolder. - Turn on the auto-save switch at the top of the card.
From then on, a note appears in the folder as soon as a meeting finishes processing.
What flavor you get
The output format follows where the folder sits, not a setting you pick. MimicScribe walks up from the destination folder looking for a vault/graph marker, so a subfolder is detected exactly like the root:
| Destination | What you get |
|---|---|
Inside an Obsidian vault (any folder under one containing .obsidian/) | YAML front matter, then the body. people:, assignees: and journal: create real graph edges (Obsidian 1.4+). Bookmarked moments get a ## Flagged Moments list that transcludes each marked turn from the transcript below it, so the words live in the note once. Action items render as Tasks-plugin checkboxes (- [ ] …), with a 📅 yyyy-MM-dd due marker when a date was extracted. |
Inside a Logseq graph (any folder under one containing logseq/) | Logseq’s native key:: value page properties as the first block, then the body. Attendees linked the same way as Obsidian. Point this at your graph’s pages folder — Logseq only indexes pages/ and journals/, so a note dropped in the graph root won’t show up in Logseq’s UI. |
| Anywhere else | The same YAML front matter + a human-readable header (Start/End/Duration/Participants) — valid Markdown everywhere, just without the vault-specific linking. |
All three flavors write the same facts; only the syntax differs. Obsidian and plain Markdown get YAML front matter (key: value), Logseq gets its own page-property block (key:: value) as the first block of the file. Here is every key, and which flavor emits it:
| Key | Obsidian | Logseq | Plain | What it holds |
|---|---|---|---|---|
title | ✓ | — | ✓ | The meeting’s display title. Logseq omits it on purpose: the page name is the filename, and title:: would override it and collide across two recordings of the same recurring meeting. |
date | ✓ | ✓ | ✓ | yyyy-MM-dd of the recording, in the meeting’s own timezone. |
start | ✓ | ✓ | ✓ | The full start timestamp. date is derivable from it and stays because queries already read it. |
timezone | ✓ | ✓ | ✓ | The IANA zone the meeting was recorded in — only when the meeting was stamped with one. Never guessed. |
imported | ✓ | ✓ | ✓ | File imports only. The day you dragged the file in, which is what the filename carries. date stays on the day the audio was made. |
duration | ✓ | ✓ | ✓ | Human-readable, e.g. "45m". |
duration_minutes | ✓ | — | ✓ | The same number, numeric, so Dataview and Bases can sort on it. |
meeting-id | ✓ | ✓ | ✓ | The stable id. This is what lets a later re-export follow the note if you rename the meeting in the app instead of leaving a duplicate behind. |
source | ✓ | ✓ | ✓ | recording, file_import, or text_import. |
source-filename | ✓ | ✓ | ✓ | The imported file’s name, when there was one. |
people | ✓ | — | — | Every voice in the recording. A name you’ve attributed is a quoted "[[Wikilink]]"; a placeholder the diarizer assigned is plain text and never linked, so it can’t mint a page for someone who doesn’t exist. |
attendees | — | ✓ | — | The same roster, comma-joined, linked the same way. |
participants | — | — | ✓ | The same roster as plain names, attributed ones first. |
assignees | ✓ | ✓ | — | Who owes something — the subset of the roster that action items are assigned to, and the one a Base can act on. |
journal | ✓ | — | — | "[[yyyy-MM-dd]]" pointing at the day’s daily note, using your vault’s own daily-note path and format. Omitted rather than guessed when that format has a token this can’t render. |
flags | ✓ | ✓ | ✓ | How many moments you bookmarked, numeric — only when there’s at least one, so flags > 0 is the query. There is deliberately no flagged tag: flags: 1 and flags: 9 would be the same tag. |
action_items | ✓ | ✓ | ✓ | How many action items the meeting produced, always present — action_items: 0 is the useful one. Underscored, not hyphenated, because Dataview reads action-items > 0 as a subtraction. |
tags | ✓ | ✓ | ✓ | See below. |
Tags are namespaced. The list is always meeting first, then the meeting’s own search tags as meeting/<tag> — not bare. So a meeting tagged “Q3 Planning” and “budget review” writes:
tags:
- meeting
- meeting/q3-planning
- meeting/budget-review Each tag is lowercased, spaces collapsed to -, and other punctuation stripped; non-ASCII letters are kept (meeting/münchen, meeting/会議室), and a tag with no letters at all is dropped because Obsidian rejects all-numeric tags. Write your Dataview queries against meeting/…, not the bare keyword. These are one-off keywords a language model invented for one meeting, and a few hundred meetings would put a few hundred of them beside your own hand-made tags in the tag pane — nested, they collapse under meeting and stay searchable, since Obsidian’s #meeting matches #meeting/roadmap too.
Offline meetings are included
An offline meeting still gets a note. Audio, transcription, and speaker ID always run on your Mac, and an offline meeting is never sent to a cloud AI. Its note is transcript-only unless you add a summary with AI that runs on your Mac, and nothing on the way from recording to the file in your vault leaves your Mac.
For an AI summary that never leaves your Mac, point your AI endpoint at a local model (Settings → AI & Data, e.g. Ollama on localhost). Online meetings then get their summary automatically, and an offline meeting can use it through Generate with Your AI. The note carries the summary and action items.
Editing a note after it’s written
MimicScribe writes the note when the meeting’s summary finishes, and keeps it in sync with the app after that — a speaker correction, a rename, or a summary you generate later all update the file in place. The moment you edit the note yourself in your vault, it’s yours: MimicScribe detects the change and never touches that file again, so nothing it does afterward can silently overwrite your edit.
Ticking a checkbox is not an edit. Action items are exported as checkboxes so you can tick them. Under a plain byte comparison, ticking one would hand the file over to you and silently stop every later correction from reaching it. So ownership is decided on a canonical projection of the file rather than its raw bytes, and a short list of marks your tools make by themselves are erased from both sides before the comparison:
- [ ]→- [x]— any status character between the brackets, including the Tasks plugin’s custom ones (/,-,>).- The Tasks plugin’s
✅ 2026-09-03/❌ 2026-09-03completion stamp on that same click. - Logseq flipping
TODO→DONE(orDOING,NOW,LATER,WAITING,CANCELED), and the:LOGBOOK:drawer it writes underneath. - Logseq’s own per-block bookkeeping —
id::,collapsed::. - A trailing
^block-id, which Obsidian appends to any line you link to from another note.
Those marks also survive the next refresh: when a line changed only in one of those ways, the file’s version of it wins and your tick is carried forward rather than reset. Everything else — a sentence you rewrote, a bullet you added, a section you deleted — still hands the note to you permanently.
The 📅 yyyy-MM-dd due marker is deliberately not on that list. MimicScribe writes it from the action item’s own due date, so ignoring it would both hide a due-date correction and freeze a stale date on your page.
Want an edited note back in sync with the app? Re-export that meeting with Export Transcript… and save Markdown over it — that’s an explicit hand-back, and auto-save picks it up again from there.
Exporting existing meetings
Just turned this on and want your back-catalog in your vault too? Click Export existing meetings… in the same card. It writes a note for every meeting you’ve already recorded — offline ones included — skipping anything still processing. It’s independent of the auto-save switch, so you can run it any time you point the folder at a new location. Safe to re-run: it refreshes any note you haven’t edited (picking up corrections, renames, or a summary generated since), and never touches one you have — so re-running after you’ve hand-edited notes in your vault won’t clobber them.
Manual one-off export
Don’t want auto-save at all, or need a note somewhere outside your usual folder? Every meeting has an Export Transcript… action (transcript detail view, sidebar right-click, or the review overlay) that opens a save panel offering six formats — Markdown, plain text, SRT and WebVTT subtitles, JSON, and CSV. See Exporting & sharing for what each one contains.
Pick Markdown and you get the vault note exactly as auto-save would have written it: same renderer, so the flavor still follows whatever folder you save into, and saving over a note you’d edited is the explicit hand-back described above. The other five formats are plain document writes — they don’t touch vault-note ownership and don’t hand anything back to auto-save.
URL import helper (yt-dlp)
There’s one more card on Settings > Integrations, and it exists because MimicScribe downloads a third-party executable onto your Mac — you’re entitled to see that it happened, know which copy is in use, and remove it.
When it’s used. A link pointing straight at a media file — an .mp3, an .m4a, an .mp4 — is downloaded directly and the helper isn’t involved. Anything else is a page: a podcast episode, a conference talk, a video with a player on it. Pulling audio out of a page is what yt-dlp does, so that’s the path that uses it. This applies to URL imports from the app and to the url argument on the MCP import tools alike.
How the copy is managed. MimicScribe downloads the helper from its official GitHub release, verifies its SHA-256 before running it, refreshes it weekly, and single-flights the whole thing so two imports can’t race. The card shows the path it resolved and the version.
If you already have your own yt-dlp — a Homebrew install, say — that one wins and MimicScribe never modifies, updates, or removes it. The card says “Using your own install” so it’s clear this app isn’t the thing managing it, and it doesn’t offer to install a second copy alongside.
Nothing installs itself. A managed copy only ever comes into existence because a person pressed the button, either on this card or on the offer that appears in the import sheet. Installing an executable needs your consent, and pressing the button is how you give it. An agent asking for a page import over MCP gets a link to the setting, never a silent install. If you’re setting up an agent, install it here first — otherwise the agent’s first page import stops to ask a question you’re not there to answer.
If MimicScribe installed it, the card has a Remove button.
Webhook
Point MimicScribe at any URL and it POSTs a JSON payload there whenever a meeting finishes , which is how you connect Zapier, Make, n8n, or anything else that can receive a webhook. It needs no agent or client software. Unlike the notes folder, this is the one lane that leaves your Mac over the network, so read What each lane sends before turning it on.
Setup
- Open Settings > Integrations > Webhook.
- Paste your endpoint URL. It must be
https://— plainhttp://is only accepted forlocalhost/127.0.0.1, for a local automation runner like a self-hosted n8n. - Optionally set a signing secret — this adds an
X-MimicScribe-Signatureheader so your endpoint can verify the request really came from MimicScribe. It’s stored in your login Keychain, not in plain preferences. - Click Send test event to POST a sample payload and confirm your endpoint receives it — the result (
HTTP 200, or the error) shows right in the card. This works before you flip the switch on, so you can verify the wiring first. - Turn the webhook on.
What fires, and when
| Event | Fires when | Notes |
|---|---|---|
meeting.completed | A meeting’s AI summary finishes | Fires promptly. |
meeting.updated | Anything about a saved meeting changes afterward | Coalesced — a burst of edits inside an ~8-second window becomes one POST, not one per edit. |
meeting.test | You click Send test event in Settings | A synthetic sample payload, not a real meeting, so your endpoint can tell it apart. |
meeting.updated is broader than “you corrected a speaker.” It hangs off the app’s internal “this meeting was edited” signal, which has around nineteen call sites, and it deliberately fires on all of them. Expect a POST from at least:
- Any speaker correction, rename, merge, swap, or removal — from the app or from
correct_meeting. - A summary regenerated, a summary generated later on an offline meeting, a Q&A correction applied.
- Renaming the meeting’s title. Nothing about the transcript changed; you’ll still get an
updated. - A moment bookmarked or unbookmarked from a saved transcript, not just during recording.
- Edits made in Settings → Speaker Profiles, including undoing a profile merge or a co-enrollment. One gesture there can touch many meetings and emit an
updatedfor each one.
That’s intentional. A webhook owns nothing downstream, so it is cheaper to fire and let you upsert than to guess which changes you care about. But size your endpoint for it: filter on event if you only want completions, and never let an updated create a second record.
Offline meetings are never sent, even if they have a summary. Their local Markdown note, if you use the folder-drop above, still gets written; that’s a local file, not network traffic.
Every payload carries the meeting’s id and the event name. Treat it as an upsert-by-id contract: apply an updated payload by overwriting whatever record you created from the matching completed payload, rather than creating a second one.
What happens if my endpoint is down?
There are no retries, so design around that. Each event is one POST with a 30-second timeout. If your endpoint is unreachable, returns a 500, or times out, that event is gone — nothing is queued, nothing is re-sent, and the app does not warn you.
I made it one-shot rather than adding a retry queue because a retry queue that survives quits and reboots is a real piece of infrastructure, and this is a webhook, not a delivery guarantee. Two things follow for you:
- The failure is visible, but you have to look. Every attempt — success or failure, with the status code or the error — is recorded in Settings → Network Log for the current session. That’s where to check when a Zap stopped firing.
- Don’t make the webhook your only copy. If a meeting has to reach your system, have the consumer reconcile against
search_meetingsor the notes folder rather than trusting that every POST landed.
Use Send test event before you rely on it. It posts a sample payload and shows the result (HTTP 200, or the error) in the card, and it works before you turn the switch on.
Payload shape
{
"event": "meeting.completed",
"meeting": {
"id": "3f2e1a9b-6c4d-4b8a-9e2f-1a2b3c4d5e6f",
"title": "Q3 Planning Sync",
"date": "2026-07-16T14:30:00Z",
"durationSeconds": 2712.4,
"tags": ["pricing", "roadmap"],
"participants": [
{ "name": "Jane Okafor", "placeholder": false },
{ "name": "Marshall", "placeholder": false },
{ "name": "Speaker 2", "placeholder": true }
],
"summary": {
"overview": "Pricing changes agreed ahead of the board review.",
"body": "The team agreed to ship the revised pricing before the board review..."
},
"actionItems": [
{
"id": "9c1e2b7a-…",
"owner": "Marshall",
"task": "Send the revised pricing deck",
"due": "2026-07-20",
"dueDescription": null,
"dueTimezone": null,
"type": "task",
"confidence": "firm"
}
],
"flaggedMoments": [
{
"id": "b7d3c8f1-…",
"offsetSeconds": 734.2,
"timestamp": "12:14",
"excerpt": "Jane Okafor: We can't commit to the June date until legal signs off."
}
]
}
} participants[].placeholder is true for a diarizer label nobody renamed (e.g. Speaker 2) — the same signal MCP agents use, so your automation can skip unresolved speakers when creating contacts.
flaggedMoments[] holds the moments you bookmarked, during the meeting or afterwards from the saved transcript, each with the lines spoken at that point. The array is empty on most meetings. A moment marked during silence arrives with an empty excerpt rather than being dropped, so your count matches what you see in the app.
The full transcript is never sent — it’s the largest and most edit-sensitive part of a meeting, deliberately excluded from network egress. A flagged moment carries only the lines at the instant you marked.
Request headers:
| Header | Value |
|---|---|
Content-Type | application/json |
X-MimicScribe-Event | meeting.completed, meeting.updated, or meeting.test (the Settings test button) |
User-Agent | MimicScribe/<version> |
X-MimicScribe-Signature | sha256=<hex HMAC-SHA256 of the raw request body> — only present if you set a signing secret |
Discord
Discord’s incoming webhooks don’t render arbitrary JSON — they need a content string or an embeds array, and ignore everything else. So when the URL you paste is a Discord webhook (https://discord.com/api/webhooks/...), MimicScribe automatically sends a Discord message embed instead of the raw payload above: the meeting title as the embed title, the summary overview as the description, and action items and tags as fields. Paste the URL, click Send test event, and a card appears in the channel — no relay or formatter in between. (A signing secret still adds the signature header; Discord ignores it, which is harmless.)
Verifying the signature
The signature is an HMAC-SHA256 of the raw request body bytes under your secret — verify against the bytes you received, not a re-serialized copy of the parsed JSON (re-serializing can reorder keys or change whitespace, changing the hash).
Node.js:
import crypto from 'node:crypto';
function isValidSignature(rawBody, signatureHeader, secret) {
if (!signatureHeader?.startsWith('sha256=')) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
const provided = signatureHeader.slice('sha256='.length);
const a = Buffer.from(expected, 'hex');
const b = Buffer.from(provided, 'hex');
return a.length === b.length && crypto.timingSafeEqual(a, b);
} Python:
import hashlib
import hmac
def is_valid_signature(raw_body: bytes, signature_header: str, secret: str) -> bool:
if not signature_header or not signature_header.startswith("sha256="):
return False
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
provided = signature_header.removeprefix("sha256=")
return hmac.compare_digest(expected, provided) Both compare with a constant-time function (crypto.timingSafeEqual / hmac.compare_digest), so a mismatched signature can’t be brute-forced by timing.
Example: Zapier
- Create a Zap with trigger Webhooks by Zapier > Catch Hook. Zapier gives you a unique URL.
- Paste that URL into MimicScribe’s webhook field and turn it on.
- Let one real meeting complete, then use Zapier’s “test trigger” so it learns the payload fields (
meeting__title,meeting__summary__body,meeting__action_items[], …). - Add a Filter step so only
event=meeting.completedcontinues, if you don’t want an action on every later edit too. - Add your action — e.g. Slack: Send Channel Message pulling in
meeting__titleandmeeting__summary__body.
Example: Make
- New scenario > Webhooks > Custom webhook > Add to generate a URL.
- Paste it into MimicScribe, enable the webhook.
- Run Determine data structure by letting one real meeting complete — Make reads the shape automatically.
- Filter the route on
eventequalsmeeting.completed. - Add a destination — e.g. Notion: Create a Database Item, mapping
meeting.title→ Name andmeeting.summary.body→ a text property, with an iterator overmeeting.actionItems[].
Example: n8n (self-hosted)
- Add a Webhook node (method
POST), copy its Production URL. Because n8n runs locally,http://localhost:…is accepted. - Paste into MimicScribe, enable the webhook, set a signing secret.
- Add a Code node to verify
X-MimicScribe-Signatureagainst the raw body (n8n runs on Node, so the crypto snippet above applies — check your n8n version’s API for raw-body access) and reject the run on mismatch. - Add an IF node checking
{{$json["event"]}} === "meeting.completed". - Wire your destination node — Notion, Google Sheets, or a task-manager node reading
actionItems.
What each lane sends, and where
MimicScribe has a few ways data can move, and only some touch the network. This doesn’t change based on which integration you use — it’s the same rule everywhere:
| Lane | What happens | Ever leaves your Mac? |
|---|---|---|
| Recording, transcription, speaker ID | Audio capture, ASR, and diarization | Never. Always on-device, whatever else you configure. |
| Text AI (summary + action items) | Runs via Gemini (default), Apple’s on-device model, a custom AI endpoint you configure, or is skipped | Your choice. Gemini and most custom endpoints are cloud calls. Apple’s on-device model runs entirely on your Mac, and a custom endpoint can point at a local model (e.g. Ollama on localhost), which also keeps it on-device. An offline meeting never uses a cloud AI here. |
| Automatic recording | Watches for a call in progress, then starts a recording | Never. Process names, call signals, and browser tab titles and addresses are all read from macOS on-device and are never sent anywhere. No audio is read by this lane at all. Reading browser tabs is part of matching a Meeting Site and happens whenever the master switch is on and you’ve granted Accessibility — there’s no separate switch for it, and turning the master switch off stops all of it. |
| Markdown notes folder | Writes a Markdown file to a folder you chose | Never — a local file write. The file’s contents reflect what the text-AI lane did for that meeting (transcript-only if offline, full summary otherwise). |
| Webhook | POSTs summary + action items + participant names to a URL you provide | Yes, by design — that’s the point. Never sends the transcript. Never fires for offline meetings. HTTPS only, except localhost. |
| MCP | Local stdio between MimicScribe and your agent process | Not by MimicScribe — the exchange is local. What your agent then does with the data (emails it, pastes it into a cloud doc, calls an API) is the agent’s own action, outside MimicScribe’s control. |
| App Intents (Spotlight, Shortcuts, Siri) | Indexes meetings into the local macOS Spotlight index; Shortcuts/Siri actions read and control your on-device library | Not by MimicScribe — the Spotlight index is a local macOS index and the actions run on-device. If you invoke an action by voice, Siri’s own speech processing is Apple’s, not MimicScribe’s. |
The short version: recording and transcription are always private. Whether a meeting’s summary touches the network is a setting you control per meeting (Offline) or globally (which AI endpoint you point at). Once a summary exists, only the webhook — and only if you turn it on — sends it anywhere; the notes folder and MCP are both local-machine operations.